# @Time : 2021/4/7 16:56
# @Author : Fioman
# @Phone : 13149920693
"""
主要是用来和生产作业相关的控制逻辑写到这里来,简单记为produce
"""
import queue
import threading

import gevent

from tools import common_tools
from tools.send_to_ui_tools import *
from vision_process.image_deal import *
from tools.save_image_to_device import *
from vision_process.cutter_image_deal import *
from vision_process.image_process_3 import *
from vision_process.image_process_1 import *

cp = ConfigParams()


class ProduceHandler(object):
    """
    生产作业相关的类,这个类主要是用来和正常的生产作业流程去控制的类.
    """

    def __init__(self, imageJoint, cutterCamCal, camZmc, cbjZmc, cutterZmc, deviceStates, offlineZmc):
        self.camZmc = camZmc
        self.cbjZmc = cbjZmc
        self.cutterZmc = cutterZmc
        self.deviceStates = deviceStates
        self.offlineZmc = offlineZmc
        self.imageJoint = imageJoint
        self.cutterCamCal = cutterCamCal
        # 4个单例的类
        self.db = DbHandler()
        self.logger = HdLogger()
        self.reload_working_info_data()
        self.boardSizeAdjustFlag = False  # 尺寸是否调整的标志位
        self.widthAdjustAfter = 0
        self.heightAdjustAfter = 0

    def produce_prepare_init(self):
        """
        生产作业开始之前的参数初始化,参数的阈值设定参数.
        :return:
        """
        self.linesQueue = queue.Queue()  # 切线队列
        # 上料线阵相机的阈值设定
        self.reload_line_cam_check_thres()
        # 上料面阵相机的阈值设定
        self.reload_cutter_cam_check_thres()
        # workingInfo参数的初始化
        self.isAllowGetImage = True  # 是否允许拍照
        self.waitBoardBack = False  # 是否等待板回退
        self.reload_trans_mtx()
        self.reload_cbj_axis_limit()  # 加载裁切机的极限幅面值
        self.upLineOffset = [0, 0, 0, 0]
        self.downLineOffset = [0, 0, 0, 0]
        self.middleLineOffset = [0, 0, 0, 0]
        self.singBoardOffset = [0, 0, 0, 0]
        self.singleBoardHeight = 0  # 单拼板首板记录的高度
        self.singleBoardWidth = 0  # 单拼板首板记录的宽度

    def reload_cbj_axis_limit(self):
        res, cbjZmcParams = self.db.get_cbj_zmc_params(self.db.cbjZmcParamsTable)
        if res != "ok":
            errorInfo = "in reload_cbj_axis_limit() error: {}".format(cbjZmcParams)
            print(errorInfo)
            self.logger.debug(errorInfo)
        else:
            self.cbjLimitX = cbjZmcParams.get("fx_max")
            self.cbjLimitY = cbjZmcParams.get("fy_max")

    def reload_trans_mtx(self):
        """
        加载标定的矩阵
        :return:
        """
        res, transMtxData = self.db.get_trans_mtx_data()
        self.transMtx = None
        if res != "ok":
            errorInfo = "in reload_trans_mtx() error: {}".format(transMtxData)
            print(errorInfo)
            self.logger.debug(errorInfo)
        else:
            self.transMtx = transMtxData.get("mtx")

    def reload_working_info_data(self):
        """
        重新加载working表的内容.
        :return:
        """
        res, workingInfo = self.db.get_working_info()
        if res == "ok":
            self.camZmcIndex = int(workingInfo.get("camZmcIndex", 0)) + 1  # 拍照控制器的索引,获取旧的,在旧的基础上加1
            self.cbjZmcIndex = self.camZmcIndex
            self.cutterZmcIndex = self.camZmcIndex
            self.indexMax = workingInfo.get("indexMax", 10000)
            self.key = workingInfo.get("orderUsedKey")

    def reload_cutter_cam_check_thres(self):
        """
        重新加载面阵相机检测的时候使用的阈值
        :return:
        """
        try:
            res, data = self.db.get_cutter_cam_check_thres()
            if res == "ok":
                self.beginAngleThres = data.get("beginAngleThres")
                self.beginAngleOffset = data.get("beginAngleOffset")
                self.cutterMoveOffset = data.get("cutterMoveOffset")
                self.finalFirstBoardThres = data.get("finalFirstBoardThres")
                self.finalSecondBoardThres = data.get("finalSecondBoardThres")
                self.finalTotalBoardThres = data.get("finalTotalBoardThres")
                self.finalFrontAngle = data.get("finalFrontAngle")
                self.finalThreeFrontAngle = data.get("finalThreeFrontAngle")
                self.finalBackAngle = data.get("finalBackAngle")
                self.finalThreeBackAngle = data.get("finalThreeBackAngle")
                self.finalFoldThres = data.get("finalFoldThres", 8)  # 终检叠板阈值
            else:
                errorInfo = data
                print(errorInfo)
                self.logger.debug(errorInfo)
        except Exception as e:
            errorInfo = "in ProduceHandler.reload_cutter_cam_check_thres() error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

    def reload_line_cam_check_thres(self):
        """
        重新加载线阵相机使用的阈值
        :return:
        """
        try:
            res, data = self.db.get_line_cam_check_thres()
            if res == "ok":
                self.totalHeightMax = data.get("totalHeightMax")
                self.totalHeightMin = data.get("totalHeightMin")
                self.calcThresBig = data.get("calcThresBig")
                self.calcThresSmall = data.get("calcThresSmall")
                self.innerToEdgeThres = data.get("innerToEdgeThres")
                self.firstBoardErrorThres = data.get("firstBoardErrorThres")
                self.secondBoardErrorThres = data.get("secondBoardErrorThres")
                self.totalBoardErrorThres = data.get("totalBoardErrorThres")
            else:
                errorInfo = data
                print(errorInfo)
                self.logger.debug(errorInfo)
        except Exception as e:
            errorInfo = "in ProduceHandler.line_cam_check_thres_reload() error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

    @staticmethod
    def reload_config_params():
        """
        重新加载设备参数
        :return:
        """
        # 重新加载那些全局的参数
        cp.reload_config_params()

    # web_request_函数,下面全部都是.
    def web_request_login(self, userInfo):
        """
        前端界面请求登陆界面.
        :param userInfo:用户信息,包含用户名和密码
        :return:
        """
        username = userInfo.get("userName", "")
        password = userInfo.get("password", "")

        if username == 'admin' and password == 'hengdingNo.1':
            res = {
                'currentAuthority': 'admin',
                'status': 'ok',
                'type': 'account'
            }
            self.db.update_login_state(True)
            return res
        userInfo = self.db.get_user_list_by_username(username)
        if len(userInfo) == 0:
            res = {
                'status': 'error',
                'errorMsg': '你输入的用户名不存在,请重新输入'
            }
            return res
        elif username == "" and password == "":
            res = {
                'status': 'error',
                'errorMsg': '输入的用户名和密码不能是空,请重新输入'
            }
            return res
        elif username != userInfo.get("username", "") or password != userInfo.get("password", ""):
            res = {
                'currentAuthority': 'guest',
                'status': 'error',
                'errorMsg': '你输入的密码错误,请重新输入',
                'type': 'account'
            }
            return res
        else:
            if userInfo.get("role", "") == "普通用户":
                res = {
                    'currentAuthority': "user",
                    'status': "ok",
                    'type': "account"
                }
            else:
                res = {
                    'currentAuthority': "serviceman",
                    'status': "ok",
                    'type': "account"
                }

            # 登录成功,刷新登录时间.
            query = {
                "username": username
            }
            tableName = "userinfo"
            newValues = {
                "$set": {
                    "lastLoginTime": common_tools.get_time_now_string()
                }
            }
            self.db.update_data(tableName, query, newValues)

            # 更新登录状态
            self.db.update_login_state(True)
            return res

    def web_request_get_line_cam_check_thres(self):
        """
        前端界面请求线阵相机识别参数阈值.
        :return:
        """
        res, data = self.db.get_line_cam_check_thres()
        return [data] if res == "ok" else []

    def web_request_save_line_cam_check_thres(self, newData):
        """
        前端界面请求保存线阵相机检测参数阈值
        :param newData: 新的参数
        :return:
        """
        calcThresBig = newData.get("calcThresBig")
        if calcThresBig < 12 or calcThresBig > 20:
            return "识别偏大阈值 范围只能是 12~20,设置失败,请重新设置"
        calcThresSmall = newData.get("calcThresSmall")
        if calcThresSmall < 8 or calcThresSmall > 15:
            return "识别偏小阈值 范围只能是 8~15,设置失败,请重新设置"
        innerToEdgeThres = newData.get("innerToEdgeThres")
        if innerToEdgeThres < 3 or innerToEdgeThres > 8:
            return "内板到边缘阈值 范围只能是 3~8,设置失败,请重新设置"
        firstBoardErrorThres = newData.get("firstBoardErrorThres")
        if firstBoardErrorThres < 8 or firstBoardErrorThres > 25:
            return "和首板对比阈值 范围只能是 8~25,设置失败,请重新设置"
        secondBoardErrorThres = newData.get("secondBoardErrorThres")
        if secondBoardErrorThres < 8 or secondBoardErrorThres > 25:
            return "首板对比第二块阈值 范围只能是 8~25,设置失败,请重新设置"
        totalBoardErrorThres = newData.get("totalBoardErrorThres")
        if totalBoardErrorThres < 8 or totalBoardErrorThres > 30:
            return "首板总长阈值 范围只能是 8~30,设置失败,请重新设置"
        res, info = self.db.save_line_cam_check_thres(newData)
        self.reload_line_cam_check_thres()  # 参数重新加载
        return res if res == "ok" else info

    def web_request_get_cutter_cam_check_thres(self):
        """
        前端界面请求面阵相机识别参数阈值
        :return:
        """
        res, data = self.db.get_cutter_cam_check_thres()
        return [data] if res == "ok" else []

    def web_request_save_cutter_cam_check_thres(self, newData):
        """
        前端界面请求保存面阵相机检测阈值
        :param newData:
        :return:
        """
        beginAngleThres = newData.get("beginAngleThres")
        if beginAngleThres < 0.1 or beginAngleThres > 0.4:
            return "起点检测角度阈值 范围只能是 0.1~0.4,设置失败,请重新设置"

        finalFirstBoardThres = newData.get("finalFirstBoardThres")
        if finalFirstBoardThres < 2 or finalFirstBoardThres > 5:
            return "终检第一块阈值 范围只能是 2~5 ,设置失败,请重新设置"

        finalSecondBoardThres = newData.get("finalSecondBoardThres")
        if finalSecondBoardThres < 3 or finalSecondBoardThres > 15:
            return "终检第二块阈值 范围只能是 3~15 ,设置失败,请重新设置"

        finalTotalBoardThres = newData.get("finalTotalBoardThres")
        if finalTotalBoardThres < 8 or finalTotalBoardThres > 20:
            return "终检总长阈值 范围只能是 8~20 ,设置失败,请重新设置"

        finalFrontAngle = newData.get("finalFrontAngle")
        if finalFrontAngle < 0.3 or finalFrontAngle > 1.5:
            return "终检前角 范围只能是 0.5~1.5 ,设置失败,请重新设置"

        finalBackAngle = newData.get("finalBackAngle")
        if finalBackAngle < 0.5 or finalBackAngle > 2:
            return "终检后角 范围只能是 8~20 ,设置失败,请重新设置"

        res, info = self.db.save_cutter_cam_check_thres(newData)
        self.reload_cutter_cam_check_thres()
        return res if res == "ok" else info

    def web_request_save_config_params(self, newData):
        """
        前端界面请求保存线阵相机的全局参数值configParams
        :param newData: 新的值
        :return:
        """
        res, info = self.db.save_config_params(newData)
        self.reload_config_params()
        return res if res == "ok" else info

    def order_info_init(self, orderInfo, isFirstBoard=True):
        """
        点击自动作业开始的时候工单尺寸初始化
        :param isFirstBoard:
        :param orderInfo: 界面上传递过来的工单信息
        :return:
        """
        self.isFirstBoard = isFirstBoard
        self.boardBreak = True if orderInfo["is_broken"] == "是" else False
        self.isFanzhuan="否"
        self.cutId = orderInfo["id"]
        self.jointNumber = orderInfo["joint_number"]
        self.firstCutSpeed, self.cutSpeed = self.db.get_cut_speed_by_cropper_thick(orderInfo["cropperThick"])
        res, knifeCutParams = self.db.get_knife_cut_params()
        if res != "ok":
            errorInfo = knifeCutParams
            print(errorInfo)
            self.logger.debug(errorInfo)
            self.cutDeep = 0
        else:
            self.cutDeep = knifeCutParams.get("cutDeep")
        self.key = int(orderInfo["key"])
        self.db.save_order_used_key(int(self.key))

        self.orderWidthMm = orderInfo["width"] * 25.4  # 工单的宽度(工单是英寸)
        self.orderHeightMm = orderInfo["height"] * 25.4  # 工单的高度(工单是英寸)
        self.calcWidthMm = orderInfo.get("calcWidth", 0)  # 识别使用的宽度
        self.calcHeightMm = orderInfo.get("calcHeight", 0)  # 识别使用的高度
        # self.orderWidthMm = orderInfo["width"]   # 工单的宽度(工单是英寸)
        # self.orderHeightMm = orderInfo["height"]   # 工单的高度(工单是英寸)
        # self.calcWidthMm = orderInfo.get("calcWidth", 0) * 25.4  # 识别使用的宽度
        # self.calcHeightMm = orderInfo.get("calcHeight", 0) * 25.4  # 识别使用的高度
        if self.calcWidthMm != 0 and self.calcHeightMm != 0:
            self.widthMm = self.calcWidthMm
            self.heightMm = self.calcHeightMm
        else:
            self.widthMm = self.orderWidthMm
            self.heightMm = self.orderHeightMm
        self.widthPix = int(self.widthMm * cp.mm2pix)
        self.heightPix = int(self.heightMm * cp.mm2pix)
        topEdge = orderInfo["topEdge"]
        leftEdge = orderInfo["leftEdge"]
        bottomEdge = orderInfo["bottomEdge"]
        rightEdge = orderInfo["rightEdge"]
        self.topPix = int(topEdge * cp.mm2pix)
        self.leftPix = int(leftEdge * cp.mm2pix)
        self.bottomPix = int(bottomEdge * cp.mm2pix)
        self.rightPix = int(rightEdge * cp.mm2pix)
        self.topMm = topEdge
        self.leftMm = leftEdge
        self.bottomMm = bottomEdge
        self.rightMm = rightEdge
        if self.jointNumber == "2":
            ret = self.cutterZmc.set_two_board_joint_flag()
            if ret != 0:
                self.keep_zmc_read_write_error(ret, 'cutterZmc', self.camZmcIndex)
                return ret
        elif self.jointNumber == "3":
            ret = self.cutterZmc.set_three_board_joint_flag()
            if ret != 0:
                self.keep_zmc_read_write_error(ret, 'cutterZmc', self.camZmcIndex)
                return ret
        else:
            ret = self.cutterZmc.set_single_board_joint_flag()
            if ret != 0:
                self.keep_zmc_read_write_error(ret, 'cutterZmc', self.camZmcIndex)
                return ret
            # 然后界面上跟新一次工单信息
        self.logger.debug("id_{} 更换工单，本工单为{}拼板".format(self.cutterZmcIndex, self.jointNumber))
        msgVal = {
            "val": self.key
        }
        send_order_state_to_ui(msgVal)

    def keep_zmc_read_write_error(self, debugMsg, errorType, boardIndex):
        """
        保存通信异常的消息.发送给前端,前端界面上要提示这些消息,并有停止和复位按钮
        :return:
        """
        if errorType == "camZmc":
            errorMsg = "拍照控制器通信异常," + debugMsg
        elif errorType == "cbjZmc":
            errorMsg = "裁切机控制器通信异常," + debugMsg
        elif errorType == "cutterZmc":
            errorMsg = "铡刀控制器通信异常," + debugMsg
        elif errorType == "cameraError":
            errorMsg = "相机抓取图片异常," + debugMsg
        else:
            errorMsg = debugMsg
        msgVal = {
            "val": "errorState",
            "errorInfo": errorMsg
        }
        self.logger.debug("id_{} {}".format(boardIndex, errorMsg))
        send_produce_state_to_ui(msgVal)

    def web_request_stop_produce(self):
        """
        界面上点击了停止自动作业.
        :return:
        """
        # 自动作业停止,前端界面上点击了停止自动作业
        self.logger.debug("id_{} 界面上点击了停止,下发停止动作,退出自动作业.camZmcIndex = {},cbjZmcIndex = {},cutterZmcIndex = {}".format(
            self.camZmcIndex, self.camZmcIndex, self.cbjZmcIndex, self.cutterZmcIndex))
        self.deviceStates.workStopFlag = True
        ret = self.cutterZmc.cutter_zmc_stop()
        if ret != 0:
            self.keep_zmc_read_write_error(ret, "cutterZmc", self.camZmcIndex)
            return ret
        return "ok"

    def web_request_reset_cutter_self(self):
        """
        界面上点击了铡刀单独复位按钮
        :return:
        """
        ret = self.cutterZmc.cutter_zmc_reset_itself()
        if ret != 0:
            self.keep_zmc_read_write_error(ret, "cutterZmc", self.camZmcIndex)
            return ret
        self.finalCheckFlag = False
        self.finalCheckModify = False
        self.beginCheckAdjustFlag = False
        self.beginCheckModify = False
        self.reload_cutter_cam_check_thres()
        return "ok"

    def web_request_close_reset_modal(self):
        """
        关闭复位提示框
        :return:
        """
        self.resetCheck = False
        msgVal = {
            "val": "resetAllModal",
            "modalVisible": False,
            "resetInfo": "关闭设备复位框..."
        }
        send_produce_state_to_ui(msgVal)
        return "ok"

    def web_request_reset_all(self):
        """
        前端界面点击了总复位按钮.
        :return:
        """
        self.logger.debug("id_{} 点击了总复位按钮,开始复位".format(self.camZmcIndex))
        ret = self.cutterZmc.cutter_zmc_reset()
        if ret != 0:
            self.keep_zmc_read_write_error(ret, "cutterZmc", self.camZmcIndex)
        msgVal = {
            "val": "resetAllModal",
            "modalVisible": True,
            "resetInfo": "设备复位中..."
        }
        send_produce_state_to_ui(msgVal)
        time.sleep(0.5)
        self.resetCheck = True
        while self.resetCheck:
            ret1, val1 = self.camZmc.is_reset_state()
            ret2, val2 = self.cbjZmc.is_reset_state()
            ret3, val3 = self.cutterZmc.is_reset_state()
            info1 = "1. 上料复位中..." if val1 == 1 else "1. 上料复位完成!"
            info2 = "2. 裁切机复位中..." if val2 == 1 else "2. 裁切复位完成!"
            info3 = "1. 铡刀复位中..." if val3 == 1 else "3. 铡刀复位完成!"
            if val1 == 0 and val2 == 0 and val3 == 0:  # 全部是复位完成状态,就退出
                self.resetCheck = False
                msgVal = {
                    "val": "resetAllModal",
                    "modalVisible": False,
                    "resetInfo": "设备复位完成..."
                }
                send_produce_state_to_ui(msgVal)
            else:
                msgVal = {
                    "val": "resetAllModal",
                    "modalVisible": True,
                    "resetInfo": info1 + " " + info2 + " " + info3
                }
                send_produce_state_to_ui(msgVal)
            gevent.sleep(0.5)
        msgVal = {
            "val": "resetAllModal",
            "modalVisible": False,
            "resetInfo": "设备复位完成..."
        }
        send_produce_state_to_ui(msgVal)

        return "ok"

    def web_request_close_produce_start_modal(self):
        """
        前端界面点击,关闭设备启动提示框.
        :return:
        """
        self.produceStartCheck = False
        return "ok"

    def web_request_disable_calc_image(self, getImageIsAllowed):
        """
        上料的拍照功能是否开启
        :param getImageIsAllowed,是否暂停拍照功能
        :return:
        """
        self.isAllowGetImage = getImageIsAllowed
        return "ok"

    def web_request_re_calc_first_board(self):
        """
        界面上点击了重新做首板的逻辑
        :return:
        """
        self.isFirstBoard = True
        return "ok"

    def web_request_change_order(self, orderInfo):
        """
        界面上点击了更换工单
        :param orderInfo:工单的信息
        :return:
        """
        if orderInfo["key"] == self.key:
            return "更换工单失败,要更换的工单和正在作业的工单相同,请去人已选择的工单是否是要更换的工单."
        self.order_info_init(orderInfo)
        orderWidth = orderInfo["width"]
        orderHeight = orderInfo["height"]
        self.logger.debug("id_{} 界面上点击了更换工单按钮,新的工单信息: key:{} 物料号:{},录入的宽高:({},{}),识别使用的宽高:({},{}),是否易断板:{}".format(
            self.camZmcIndex, self.key, self.cutId, orderWidth, orderHeight, self.widthMm, self.heightMm, self.boardBreak))
        return "ok"

    def web_request_start_produce1(self, orderInfo):
        """
        前端界面点击了自动作业开启的按钮.
        :param orderInfo:开启自动作业的工单信息
        :return:
        """
        # 1.自动作业状态初始化
        self.produce_prepare_init()
        self.order_info_init(orderInfo)
        produceInitInfo = "id_{} 自动作业参数初始化完成,参数值如下:\n".format(self.camZmcIndex)
        produceInitInfo += "id_{} 物料号:{} 工单参数: width: {},height: {},第一刀留宽: {},第二刀留宽: {},第三刀留宽: {},第四刀留宽: {},易断板: {}\n".format(
            self.camZmcIndex, self.cutId, self.widthMm, self.heightMm, self.topMm, self.leftMm, self.bottomMm, self.rightMm, self.boardBreak)
        produceInitInfo += "id_{} 识别阈值: 偏大: {},偏小: {},内板位置: {},物料高度Max: {},物料高度Min: {},首板对比1: {},首板对比2: {},首板对比总: {}\n".format(
            self.camZmcIndex, self.calcThresBig, self.calcThresSmall, self.innerToEdgeThres, self.totalHeightMax, self.totalHeightMin,
            self.firstBoardErrorThres, self.secondBoardErrorThres, self.totalBoardErrorThres)
        produceInitInfo += "id_{} 铡刀阈值: 首检角度: {},移动补偿: {},角度补偿: {},终检第一块: {},终检第二块: {},终检前角: {},终检后角: {}\n".format(
            self.camZmcIndex, self.beginAngleThres, self.cutterMoveOffset, self.beginAngleOffset, self.finalFirstBoardThres,
            self.finalSecondBoardThres,
            self.finalFrontAngle, self.finalBackAngle)
        produceInitInfo += "id_{} 切割参数: 下刀深度: {},切割速度: 第一刀: {},剩余刀: {},裁切机极限X: {},裁切机极限Y: {}".format(
            self.camZmcIndex, self.cutDeep, self.firstCutSpeed, self.cutSpeed, self.cbjLimitX, self.cbjLimitY)
        print(produceInitInfo)
        self.logger.debug(produceInitInfo)
        # 2.启动设备
        ret = self.cutterZmc.cutter_zmc_start()
        if ret != 0:
            self.keep_zmc_read_write_error(ret, "cutterZmc", self.camZmcIndex)
            return ret
        # # 3.设置是单拼还是双拼
        # if self.jointNumber == "2":
        #     ret = self.cutterZmc.set_2pin_flag()
        #     if ret != 0:
        #         self.keep_zmc_read_write_error(ret, 'cutterZmc', self.camZmcIndex)
        #         return ret
        # elif self.jointNumber == "3":
        #     ret = self.cutterZmc.set_3pin_flag()
        #     if ret != 0:
        #         self.keep_zmc_read_write_error(ret, 'cutterZmc', self.camZmcIndex)
        #         return ret
        # else:
        #     ret = self.cutterZmc.set_1pin_flag()
        #     if ret != 0:
        #         self.keep_zmc_read_write_error(ret, 'cutterZmc', self.camZmcIndex)
        #         return ret
        self.logger.debug("id_{} 开始启动作业，本工单为{}拼板".format(self.cutterZmcIndex, self.jointNumber))
        # 4.启动提示框打开
        msgVal = {
            "val": "produceStartModal",
            "modalVisible": True,
            "produceStartInfo": "设备启动中..."
        }
        send_produce_state_to_ui(msgVal)
        gevent.sleep(0.5)
        self.produceStartCheck = True
        while self.produceStartCheck:
            _, val1 = self.camZmc.is_running()
            _, val2 = self.cbjZmc.is_running()
            _, val3 = self.cutterZmc.is_running()
            info1 = "1. 上料启动中..." if val1 == 0 else "1. 上料启动成功!"
            info2 = "2. 裁切机启动中..." if val2 == 0 else "2. 裁切机启动成功!"
            info3 = "3. 铡刀启动中..." if val3 == 0 else "3. 铡刀启动成功!"
            if val1 == 1 and val2 == 1 and val3 == 1:
                self.produceStartCheck = False
                msgVal = {
                    "val": "produceStartModal",
                    "modalVisible": False,
                    "produceStartInfo": ""
                }
            else:
                msgVal = {
                    "val": "produceStartModal",
                    "modalVisible": True,
                    "produceStartInfo": info1 + " " + info2 + " " + info3
                }
            send_produce_state_to_ui(msgVal)
            gevent.sleep(0.1)

        msgVal = {
            "val": "produceStartModal",
            "modalVisible": False,
            "produceStartInfo": ""
        }
        send_produce_state_to_ui(msgVal)

        # 自动作业启动成功以后,开启后台的线程.
        self.deviceStates.workStopFlag = False
        self.produceListenerThread = threading.Thread(target=self.produce_listener, name="produceListenerThread")
        self.produceListenerThread.setDaemon(True)
        self.produceListenerThread.start()
        return "ok"

    def produce_listener(self):
        """
        自动作业监听线程函数,主要用来监控自动作业过程中的各个作业函数的任务.
        :return:
        """
        self.logger.debug("自动作业监听线程开启!")
        self.camZmcTaskThead = threading.Thread(target=self.cam_zmc_task, name="camZmcTaskThead")
        self.cbjZmcTaskThead = threading.Thread(target=self.cbj_zmc_task, name="cbjZmcTaskThead")
        self.cutterZmcTaskThread = threading.Thread(target=self.cutter_zmc_task, name="cutterZmcTaskThread")
        self.camZmcTaskThead.setDaemon(True)
        self.cbjZmcTaskThead.setDaemon(True)
        self.cutterZmcTaskThread.setDaemon(True)
        self.camZmcTaskThead.start()
        self.cbjZmcTaskThead.start()
        self.cutterZmcTaskThread.start()

        while not self.deviceStates.workStopFlag:
            gevent.sleep(0.5)
        # 防止线程掉线
        camZmcTaskThread = self.camZmcTaskThead
        cbjZmcTaskThread = self.cbjZmcTaskThead
        cutterZmcTaskThread = self.cutterZmcTaskThread
        gevent.sleep(1)
        if camZmcTaskThread.is_alive():
            common_tools.stop_thread(camZmcTaskThread)
        if cbjZmcTaskThread.is_alive():
            common_tools.stop_thread(cbjZmcTaskThread)
        if cutterZmcTaskThread.is_alive():
            common_tools.stop_thread(cutterZmcTaskThread)
        self.logger.debug("检测到自动作业退出标志,强制结束各个作业线程,上料:{},裁切:{},铡刀:{}".format(self.camZmcIndex,
                                                                                                       self.cbjZmcIndex, self.cutterZmcIndex))

    def web_request_cutter_begin_check_pass(self):
        self.logger.debug("id_{} 起点检测: 界面上点击了确认起点检测通过".format(self.cutterZmcIndex))
        self.cutterZmc.open_door_alarm()  # 免得某个地方又忘记关了.
        ret = self.cutterZmc.set_allow_begin_check_again_flag()
        self.manualCheckAngleOk = True
        gevent.sleep(0.3)
        self.beginCheckModify = False
        if ret != 0:
            self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
            return
        return "ok"

    def cutter_zmc_task(self):
        """
        铡刀作业函数
        :return:
        """
        self.logger.debug("铡刀作业任务函数开启")
        self.finalCheckFlag = False
        self.beginCheckAdjustFlag = False
        self.finalCheckModify = False
        self.manualCheckAngleOk = False  # 人工确认角度OK
        self.staffConfirmed = False  # 人工确认铡板的状态
        while True:
            if self.deviceStates.workStopFlag:
                self.logger.debug("id_{} workStopFlag = True,铡刀作业任务函数退出.".format(self.camZmcIndex))
                return
            ret, val = self.cutterZmc.get_allow_take_pic_begin_check()
            if ret != 0:
                self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                return
            if val == 1:
                # 每次收到检测的时候,都打开门报警
                newData = {
                    "cutterZmcIndex": self.cutterZmcIndex
                }
                self.update_working_info_to_db_and_web(newData)
                self.cutterZmc.set_allow_take_pic_begin_check_clear()
                self.logger.debug("id_{} 起点检测: 收到拍照信号,1号相机开始拍照".format(self.cutterZmcIndex))
                self.cutterZmc.open_door_alarm()  # 免得某个地方又忘记关了.
                self.cutterImage1 = None
                res, self.cutterImage1 = self.cutterCamCal.get_cutter_image(1)
                if res != "ok":
                    self.logger.debug("id_{} 起点检测: 1号相机拍照失败,重新打开相机开始拍照".format(self.cutterZmcIndex))
                    res, self.cutterImage1 = self.cutterCamCal.get_cutter_image(1)
                    if res != "ok":
                        self.keep_zmc_read_write_error(self.cutterImage1, "cameraError", self.cutterZmcIndex)
                        return
                endwith = "adjust" if self.beginCheckAdjustFlag else "first"
                self.light1First, self.light2First, imageRes = get_first_thres_point_begain_check(self.cutterImage1, self.cutterCamCal.light1Pos,
                                                                                                  self.cutterCamCal.light2Pos)
                save_begin_check_image(self.cutterZmcIndex, self.cutterImage1, endwith=endwith)
                save_begin_check_res_image(self.cutterZmcIndex, imageRes, endwith=endwith)
                if self.light1First is None or self.light2First is None:
                    cutterHintInfo = "有灯管全黑,这个时候考虑是冲板,铡刀后移50个mm,再检测一次."
                    self.logger.debug("id_{} 起点检测: {}".format(self.cutterZmcIndex, cutterHintInfo))
                    gevent.sleep(1)
                    res, self.cutterImage1 = self.cutterCamCal.get_cutter_image(1)
                    self.light1First, self.light2First, imageRes = get_first_thres_point_begain_check(self.cutterImage1, self.cutterCamCal.light1Pos,
                                                                                                      self.cutterCamCal.light2Pos)
                    save_begin_check_image(self.cutterZmcIndex, self.cutterImage1, endwith=endwith)
                    save_begin_check_res_image(self.cutterZmcIndex, imageRes, endwith=endwith)
                    if self.light1First is None or self.light2First is None:
                        cutterHintInfo = "后退了50个mm依旧检测到有黑色的灯管,弹框提示工人操作"
                        self.logger.debug("id_{} 起点检测: {}".format(self.cutterZmcIndex, cutterHintInfo))
                        self.beginCheckModify = True
                        self.send_cutter_begin_check_modal_to_web(imageRes, cutterHintInfo)
                        while self.beginCheckModify:
                            gevent.sleep(0.3)
                            continue
                        gevent.sleep(0.5)
                        continue

                light1Offset, light2Offset, angleCheck = self.cutterCamCal.get_offset_to_cutter_by_ruler(1, self.light1First, self.light2First)
                angleAdjust = round(angleCheck + self.topMidAngle, 2)
                angleAdjustReal = angleAdjust + self.beginAngleOffset  # 加上角度补偿
                self.logger.debug("id_{} 起点检测: 1号灯管 = {},2号灯管 = {},角度: check = {},topM = {},sum = {},角度补偿 = {}".format(self.cutterZmcIndex, light1Offset,
                                                                                                                                       light2Offset, angleCheck,
                                                                                                                                       self.topMidAngle,
                                                                                                                                       angleAdjustReal, self.beginAngleOffset))
                if (angleAdjustReal > self.beginAngleThres or angleAdjustReal < -self.beginAngleThres) and (not self.manualCheckAngleOk):
                    if not self.beginCheckAdjustFlag:
                        ret = self.cutterZmc.set_begin_check_angle_offset(angleAdjustReal)
                        if ret != 0:
                            self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                            return
                        ret = self.cutterZmc.set_begin_check_angle_fail()
                        if ret != 0:
                            self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                            return
                        self.logger.debug(
                            "id_{} 起点检测: 验证失败,进行角度调整,调整角度 = {},检测角度 = {},角度补偿 = {}".format(self.cutterZmcIndex, angleAdjustReal, angleAdjust,
                                                                                                                     self.beginAngleOffset))
                        self.beginAngleThres += 0.3
                        self.beginCheckAdjustFlag = True
                        gevent.sleep(0.05)
                        continue
                    else:
                        # 如果这里调整完之后,检测依旧没有通过,人工弹窗提示工人进行操作.正值逆时针调整,负值顺时针调整
                        if angleAdjustReal > 0:
                            cutterHintInfo = "id_{} 起点检测: 角度调整完之后,依旧有角度,请人工逆时针调整,角度 = {}".format(self.cutterZmcIndex, angleAdjust)
                        else:
                            cutterHintInfo = "id_{} 起点检测: 角度调整完之后,依旧有角度,请人工调整顺时针调整,角度 = {}".format(self.cutterZmcIndex, angleAdjust)
                        self.logger.debug(cutterHintInfo)
                        self.beginCheckModify = True
                        self.send_cutter_begin_check_modal_to_web(imageRes, cutterHintInfo)
                        while self.beginCheckModify and not self.deviceStates.workStopFlag:
                            gevent.sleep(0.2)
                            continue
                        gevent.sleep(0.5)
                        continue
                else:
                    manualInfo = "人工确认" if self.manualCheckAngleOk else "非人工确认"
                    if self.manualCheckAngleOk:
                        self.manualCheckAngleOk = False
                    beginCheckToCutter = round((light1Offset + light2Offset) / 2, 2)
                    cutterMoveDis = round(beginCheckToCutter + self.topToMiddle + self.cutterMoveOffset, 2)
                    ret = self.cutterZmc.set_begin_check_move_distance(cutterMoveDis)
                    if ret != 0:
                        self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                        return
                    ret = self.cutterZmc.set_begin_check_angle_ok()
                    if ret != 0:
                        self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                        return
                    self.logger.debug("id_{} 起点检测: 验证成功,角度 = {},板边到铡刀距离:{},第一块板长:{},移动距离:{},移动补偿:{}, {}".format(
                        self.cutterZmcIndex, angleAdjust, beginCheckToCutter, self.topToMiddle, cutterMoveDis, self.cutterMoveOffset, manualInfo))
                    self.beginCheckAdjustFlag = False
                    self.finalCheckFlag = True
                    self.reload_cutter_cam_check_thres()

                secondAdjust = False  # 是否进行了二次调整的标志.
                self.finalCheckTimes = 1  # 终检第几次,主要针对三拼板
                while self.finalCheckFlag:
                    if self.deviceStates.workStopFlag:
                        self.logger.debug("id_{} workStopFlag = True,铡刀作业任务函数退出.".format(self.camZmcIndex))
                        return
                    ret, val = self.cutterZmc.get_allow_take_pic_final_check()
                    if ret != 0:
                        self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                        return
                    if val == 1:
                        if self.jointNumber == "2":
                            self.cutterZmc.set_allow_take_pic_final_check_clear()
                            self.logger.debug("id_{} 两拼板，终点检测: 收到拍照信号,开始拍照".format(self.cutterZmcIndex))
                            res, self.cutterImage1 = self.cutterCamCal.get_cutter_image(1)
                            if res != "ok":
                                self.logger.debug("id_{} 两拼板，终点检测: 1号相机拍照失败,重新打开相机开始拍照".format(self.cutterZmcIndex))
                                res, self.cutterImage1 = self.cutterCamCal.get_cutter_image(1)
                                if res != "ok":
                                    self.keep_zmc_read_write_error(self.cutterImage1, "cameraError", self.cutterZmcIndex)
                                    return
                            res, self.cutterImage2 = self.cutterCamCal.get_cutter_image(2)
                            if res != "ok":
                                self.logger.debug("id_{} 两拼板，终点检测: 2号相机拍照失败,重新打开相机开始拍照".format(self.cutterZmcIndex))
                                res, self.cutterImage2 = self.cutterCamCal.get_cutter_image(2)
                                if res != "ok":
                                    self.keep_zmc_read_write_error(self.cutterImage2, "cameraError", self.cutterZmcIndex)
                                    return
                            finalLight1First, finalLight2First, imgRes1 = get_first_thres_point_final_check_cam_1(self.cutterImage1,
                                                                                                                  self.cutterCamCal.light1Pos,
                                                                                                                  self.cutterCamCal.light2Pos)
                            finalLight3First, finalLight4First, imgRes2 = get_first_thres_point_final_check_cam_2(self.cutterImage2,
                                                                                                                  self.cutterCamCal.light3Pos,
                                                                                                                  self.cutterCamCal.light4Pos)
                            # 保存图片
                            save_final_check_image(self.cutterZmcIndex, self.cutterImage1, self.cutterImage2)
                            save_final_check_image_res(self.cutterZmcIndex, imgRes1, imgRes2)

                            if finalLight1First is None or finalLight2First is None or finalLight3First is None or finalLight4First is None:
                                if finalLight1First is None or finalLight2First is None:
                                    self.imageSend = imgRes1
                                else:
                                    self.imageSend = imgRes2
                                cutterHintInfo = "id_{} 两拼板，终点检测: 检测到某个灯管全黑,请人工确认!"
                                self.logger.debug(cutterHintInfo)
                                self.send_cutter_final_check_modal_to_web(self.imageSend, cutterHintInfo)
                                self.finalCheckModify = True
                                while self.finalCheckModify:
                                    gevent.sleep(0.5)
                                    continue
                                gevent.sleep(0.5)
                                continue

                            # 如果找到了灯管的遮挡位置,就开始计算距离和角度
                            light1Offset, light2Offset, backAngle = self.cutterCamCal.get_offset_to_cutter_by_ruler(1, finalLight1First,
                                                                                                                    finalLight2First)
                            light3Offset, light4Offset, frontAngle = self.cutterCamCal.get_offset_to_cutter_by_ruler(2, finalLight3First,
                                                                                                                     finalLight4First)
                            firstBoardCheck = round((light3Offset + light4Offset) / 2, 2)
                            frontAngleCheck = round(frontAngle + self.topMidAngle, 2)
                            secondBoardCheck = round((light1Offset + light2Offset) / 2, 2)
                            backAngleCheck = round(abs(backAngle) - abs(self.bottomMidAngle), 2)
                            firstBoardError = round(firstBoardCheck - self.topToMiddle, 2)
                            secondBoardError = round(secondBoardCheck - (self.topToBottom - self.topToMiddle), 2)
                            totalBoardError = round(firstBoardCheck + secondBoardCheck - self.topToBottom, 2)
                            self.logger.debug("id_{} 两拼板，终点检测: 2号相机:3号灯管={:.2f},4号灯管={:.2f},第一块板长={:.2f},角度={:.2f},误差={:.2f}".format(
                                self.cutterZmcIndex, light3Offset, light4Offset, firstBoardCheck, frontAngleCheck, firstBoardError))
                            self.logger.debug("id_{} 两拼板，终点检测: 1号相机:1号灯管={:.2f},2号灯管={:.2f},第二块板长={:.2f},角度={:.2f},误差={:.2f}".format(
                                self.cutterZmcIndex, light1Offset, light2Offset, secondBoardCheck, backAngleCheck, secondBoardError))
                            if not self.boardBreak:
                                # 如果不是易断板
                                if ((abs(frontAngleCheck) > self.finalFrontAngle or abs(firstBoardError) > self.finalFirstBoardThres or
                                     abs(backAngleCheck) > self.finalBackAngle or abs(secondBoardError) > self.finalSecondBoardThres)) and (
                                        not self.staffConfirmed):
                                    if secondAdjust:
                                        debugInfo = "id_{} 两拼板，终点检测: 二次调整后,验证失败,前角:{},后角:{},第一块偏:{},第二块偏:{},总偏差:{} ".format(
                                            self.cutterZmcIndex, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError, totalBoardError)
                                    else:
                                        debugInfo = "id_{} 两拼板，终点检测: 二次调整前,验证失败,前角:{},后角:{},第一块偏:{},第二块偏:{},总偏差:{} ".format(
                                            self.cutterZmcIndex, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError, totalBoardError)
                                    self.logger.debug(debugInfo)
                                    if abs(frontAngleCheck) > self.finalFrontAngle or abs(backAngleCheck) > self.finalBackAngle or \
                                            abs(secondBoardError) > self.finalSecondBoardThres:
                                        # 两拼板，终点检测,非第一块板长引起的,要给这个个信息发送给前端
                                        self.logger.debug("id_{} 两拼板，终点检测: 验证出错,弹窗提示工人操作.".format(self.cutterZmcIndex))
                                        if abs(frontAngleCheck) > self.finalFrontAngle:
                                            self.imageSend = imgRes2
                                        else:
                                            self.imageSend = imgRes1
                                        self.send_cutter_final_check_modal_to_web(self.imageSend, debugInfo)
                                        self.finalCheckModify = True
                                        while self.finalCheckModify:
                                            gevent.sleep(0.5)
                                            continue
                                        gevent.sleep(0.5)
                                        continue
                                    else:
                                        if not secondAdjust:
                                            self.logger.debug("id_{} 两拼板，终点检测: 进行二次调整,调整的值 = {}".format(self.cutterZmcIndex, -firstBoardError))
                                            cutterFinalAdjust = -firstBoardError
                                            ret = self.cutterZmc.set_final_check_second_adjust_distance(cutterFinalAdjust)
                                            if ret != 0:
                                                self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                                                return
                                            ret = self.cutterZmc.set_final_check_second_adjust_flag()
                                            if ret != 0:
                                                self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                                                return
                                            secondAdjust = True
                                            continue
                                        else:
                                            debugInfo = "id_{} 两拼板，终点检测: 二次调整后仍然没有通过.第一块偏差:{},阈值:{}".format(
                                                self.cutterZmcIndex, firstBoardError, self.firstBoardErrorThres)
                                            self.logger.debug(debugInfo)
                                            self.imageSend = imgRes2
                                            self.send_cutter_final_check_modal_to_web(self.imageSend, debugInfo)
                                            self.finalCheckModify = True
                                            while self.finalCheckModify:
                                                gevent.sleep(0.5)
                                                continue
                                            if self.staffConfirmed:
                                                continue
                                            gevent.sleep(0.5)
                                            continue
                                # 如果验证通过
                                else:
                                    manualInfo = "人工确认" if self.staffConfirmed else "非人工确认"
                                    if self.staffConfirmed:
                                        self.staffConfirmed = False
                                    if secondAdjust:
                                        debugInfo = "id_{} 两拼板，终点检测: 二次调整后,{}验证通过,前角:{},后角:{},第一块偏:{},第二块偏:{},总偏差:{} ".format(
                                            self.cutterZmcIndex, manualInfo, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError,
                                            totalBoardError)
                                    else:
                                        debugInfo = "id_{} 两拼板，终点检测: 二次调整前,{}验证通过,前角:{},后角:{},第一块偏:{},第二块偏:{},总偏差:{} ".format(
                                            self.cutterZmcIndex, manualInfo, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError,
                                            totalBoardError)
                                    self.logger.debug(debugInfo)
                                    ret = self.cutterZmc.set_final_check_result_ok()
                                    if ret != 0:
                                        self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                                        return
                                    else:
                                        self.finalCheckFlag = False
                                        self.finalCheckModify = False

                            # 如果是易断板
                            else:
                                if ((abs(frontAngleCheck) > self.finalFrontAngle) or abs(firstBoardError) > self.finalFirstBoardThres
                                    or secondBoardError < -(self.finalSecondBoardThres - 5)) and (not self.staffConfirmed):
                                    if secondAdjust:
                                        debugInfo = "id_{} 两拼板，终点检测: 二次调整后,验证失败,易断板,前角:{},后角:{},第一块偏:{},第二块偏:{},总偏差:{} ".format(
                                            self.cutterZmcIndex, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError, totalBoardError)
                                    else:
                                        debugInfo = "id_{} 两拼板，终点检测: 二次调整前,验证失败,易断板,前角:{},后角:{},第一块偏:{},第二块偏:{},总偏差:{} ".format(
                                            self.cutterZmcIndex, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError, totalBoardError)
                                    self.logger.debug(debugInfo)
                                    if abs(frontAngleCheck) > self.finalFrontAngle:
                                        cutterHintInfo = "id_{} 两拼板，终点检测: 易断板,前角超过阈值.检测值: {},阈值: {}".format(self.cutterZmcIndex,
                                                                                                                                 frontAngleCheck, self.finalFrontAngle)
                                        self.logger.debug(cutterHintInfo)
                                        self.imageSend = imgRes2
                                        self.send_cutter_final_check_modal_to_web(self.imageSend, cutterHintInfo)
                                        self.finalCheckModify = True
                                        while self.finalCheckModify:
                                            gevent.sleep(0.3)
                                            continue
                                        gevent.sleep(0.3)
                                        continue
                                    elif (secondBoardError + firstBoardError) < -self.finalFoldThres:
                                        cutterHintInfo = "id_{} 两拼板，终点检测: 易断板,检测到叠板.检测值: {},阈值: {}".format(self.cutterZmcIndex,
                                                                                                                               secondBoardError, self.finalFoldThres)
                                        self.logger.debug(cutterHintInfo)
                                        self.imageSend = imgRes1
                                        self.send_cutter_final_check_modal_to_web(self.imageSend, cutterHintInfo)
                                        self.finalCheckModify = True
                                        while self.finalCheckModify:
                                            gevent.sleep(0.3)
                                            continue
                                        gevent.sleep(0.3)
                                        continue
                                    else:
                                        if not secondAdjust:
                                            self.logger.debug("id_{} 两拼板，终点检测: 易断板,进行二次调整,调整的值 = {}".format(
                                                self.cutterZmcIndex, -firstBoardError))
                                            cutterFinalAdjust = -firstBoardError
                                            ret = self.cutterZmc.set_final_check_second_adjust_distance(cutterFinalAdjust)
                                            if ret != 0:
                                                self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                                                return
                                            ret = self.cutterZmc.set_final_check_second_adjust_flag()
                                            if ret != 0:
                                                self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                                                return
                                            secondAdjust = True
                                            continue
                                        else:
                                            debugInfo = "id_{} 两拼板，终点检测: 易断板,二次调整后仍然没有通过.第一块偏差:{},阈值:{}".format(
                                                self.cutterZmcIndex, firstBoardError, self.firstBoardErrorThres)
                                            self.logger.debug(debugInfo)
                                            self.imageSend = imgRes2
                                            self.send_cutter_final_check_modal_to_web(self.imageSend, debugInfo)
                                            self.finalCheckModify = True
                                            while self.finalCheckModify:
                                                gevent.sleep(0.5)
                                                continue
                                            if self.staffConfirmed:
                                                continue
                                            gevent.sleep(0.5)
                                            continue

                                else:
                                    manualInfo = "人工确认" if self.staffConfirmed else "非人工确认"
                                    if self.staffConfirmed:
                                        self.staffConfirmed = False
                                    if secondAdjust:
                                        debugInfo = "id_{} 两拼板，终点检测: 二次调整后,易断板,{}验证通过,前角:{},后角:{},第一块偏:{},第二块偏:{},总偏差:{} ".format(
                                            self.cutterZmcIndex, manualInfo, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError,
                                            totalBoardError)
                                    else:
                                        debugInfo = "id_{} 两拼板，终点检测: 二次调整前,易断板,{}验证通过,易断板,前角:{},后角:{},第一块偏:{},第二块偏:{},总偏差:{} ".format(
                                            self.cutterZmcIndex, manualInfo, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError,
                                            totalBoardError)
                                    self.logger.debug(debugInfo)
                                    ret = self.cutterZmc.set_final_check_result_ok()
                                    if ret != 0:
                                        self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                                        return
                                    else:
                                        self.finalCheckFlag = False
                                        self.finalCheckModify = False
                        elif self.jointNumber == "3":
                            self.cutterZmc.set_allow_take_pic_final_check_clear()
                            self.logger.debug("id_{} 三拼板，终点检测: 收到拍照信号,开始拍照".format(self.cutterZmcIndex))
                            res, self.cutterImage1 = self.cutterCamCal.get_cutter_image(1)
                            if res != "ok":
                                self.logger.debug("id_{} 三拼板，终点检测: 1号相机拍照失败,重新打开相机开始拍照".format(self.cutterZmcIndex))
                                res, self.cutterImage1 = self.cutterCamCal.get_cutter_image(1)
                                if res != "ok":
                                    self.keep_zmc_read_write_error(self.cutterImage1, "cameraError", self.cutterZmcIndex)
                                    return
                            res, self.cutterImage2 = self.cutterCamCal.get_cutter_image(2)
                            if res != "ok":
                                self.logger.debug("id_{} 三拼板，终点检测: 2号相机拍照失败,重新打开相机开始拍照".format(self.cutterZmcIndex))
                                res, self.cutterImage2 = self.cutterCamCal.get_cutter_image(2)
                                if res != "ok":
                                    self.keep_zmc_read_write_error(self.cutterImage2, "cameraError", self.cutterZmcIndex)
                                    return
                            finalLight1First, finalLight2First, imgRes1 = get_first_thres_point_final_check_cam_1(self.cutterImage1,
                                                                                                                  self.cutterCamCal.light1Pos,
                                                                                                                  self.cutterCamCal.light2Pos)
                            finalLight3First, finalLight4First, imgRes2 = get_first_thres_point_final_check_cam_2(self.cutterImage2,
                                                                                                                  self.cutterCamCal.light3Pos,
                                                                                                                  self.cutterCamCal.light4Pos)

                            # 保存图片
                            save_three_final_check_image(self.cutterZmcIndex, self.cutterImage1, self.cutterImage2)
                            save_three_final_check_image_res(self.cutterZmcIndex, imgRes1, imgRes2)

                            if finalLight1First is None or finalLight2First is None or \
                                    (finalLight3First is None and self.finalCheckTimes == 2) or (
                                    finalLight4First is None and self.finalCheckTimes == 2):
                                if finalLight1First is None or finalLight2First is None:
                                    self.imageSend = imgRes1
                                elif finalLight3First is None or finalLight4First is None:
                                    self.imageSend = imgRes2
                                cutterHintInfo = "id_{} 三拼板，第二次终点检测: 检测到铡刀相机下某个灯管全黑,请人工确认!"
                                self.logger.debug(cutterHintInfo)
                                self.send_cutter_final_check_modal_to_web(self.imageSend, cutterHintInfo)
                                self.finalCheckModify = True
                                while self.finalCheckModify:
                                    gevent.sleep(0.5)
                                    continue
                                continue

                            # 如果找到了灯管的遮挡位置,就开始计算距离和角度
                            light1Offset, light2Offset, backAngle = self.cutterCamCal.get_offset_to_cutter_by_ruler(1, finalLight1First,
                                                                                                                    finalLight2First)
                            light3Offset, light4Offset, frontAngle = self.cutterCamCal.get_offset_to_cutter_by_ruler(2, finalLight3First,
                                                                                                                     finalLight4First)
                            firstBoardCheck = round((light3Offset + light4Offset) / 2, 2)
                            frontAngleCheck = round(frontAngle + self.topMidAngle, 2)
                            secondBoardCheck = round((light1Offset + light2Offset) / 2, 2)
                            backAngleCheck = round(abs(backAngle) - abs(self.bottomMidAngle), 2)
                            if self.finalCheckTimes == 1:  # 如果是第一次终点检测
                                print(firstBoardCheck, self.firstBoardLen, self.topToMiddle)
                                firstBoardError = round(firstBoardCheck - self.firstBoardLen, 2)
                                secondBoardError = round(secondBoardCheck - (self.secondBoardLen + self.thirdBoardLen), 2)
                            else:
                                firstBoardError = round(firstBoardCheck - self.secondBoardLen, 2)
                                secondBoardError = round(secondBoardCheck - self.thirdBoardLen, 2)
                            # totalBoardError = round(firstBoardCheck + secondBoardCheck - self.topToBottom, 2)

                            self.logger.debug("id_{} 三拼板，第 {} 次终点检测: 2号相机:3号灯管={:.2f},4号灯管={:.2f},第一块板长={:.2f},角度={:.2f},误差={:.2f}".format(
                                self.cutterZmcIndex, self.finalCheckTimes, light3Offset, light4Offset, firstBoardCheck, frontAngleCheck, firstBoardError))
                            self.logger.debug("id_{} 三拼板，第 {} 次终点检测: 1号相机:1号灯管={:.2f},2号灯管={:.2f},第二块板长={:.2f},角度={:.2f},误差={:.2f}".format(
                                self.cutterZmcIndex, self.finalCheckTimes, light1Offset, light2Offset, secondBoardCheck, backAngleCheck, secondBoardError))

                            # 如果不是易断板
                            if not self.boardBreak:
                                if ((abs(frontAngleCheck) > self.finalFrontAngle and self.finalCheckTimes == 1) or
                                    (abs(firstBoardError) > self.finalFirstBoardThres and self.finalCheckTimes == 1) or
                                    (abs(backAngleCheck) > self.finalThreeBackAngle and self.finalCheckTimes == 2) or
                                    (abs(frontAngleCheck) > self.finalThreeFrontAngle and self.finalCheckTimes == 2) or
                                    (abs(secondBoardError) > self.finalSecondBoardThres and self.finalCheckTimes == 2)) and (not self.staffConfirmed):

                                    debugInfo = "id_{}  第 {} 次终点检测验证失败,前角:{:.2f},后角:{:.2f},第一块偏:{:.2f},第二块偏:{:.2f},两板相差:{:.2f}, 非易断板".format(
                                        self.cutterZmcIndex, self.finalCheckTimes, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError,
                                        (firstBoardError + secondBoardError))
                                    self.logger.debug(debugInfo)
                                    if (abs(frontAngleCheck) > self.finalFrontAngle and self.finalCheckTimes == 1) or \
                                            (abs(backAngleCheck) > self.finalThreeBackAngle and self.finalCheckTimes == 2) or \
                                            (abs(frontAngleCheck) > self.finalThreeFrontAngle and self.finalCheckTimes == 2) or \
                                            ((firstBoardError + secondBoardError) > self.finalTotalBoardThres and self.finalCheckTimes == 2):

                                        self.logger.debug("id_{} 三拼板，第 {} 次终点检测: 验证出错,弹窗提示工人操作.".format(self.cutterZmcIndex, self.finalCheckTimes, ))
                                        if abs(frontAngleCheck) > self.finalFrontAngle:
                                            self.imageSend = imgRes2
                                        else:
                                            self.imageSend = imgRes1
                                        self.send_cutter_final_check_modal_to_web(self.imageSend, debugInfo)
                                        self.finalCheckModify = True
                                        while self.finalCheckModify:
                                            gevent.sleep(0.5)
                                            continue
                                        if self.staffConfirmed:
                                            continue
                                        gevent.sleep(0.5)
                                        continue
                                    else:
                                        if not secondAdjust:
                                            if self.finalCheckTimes == 1:
                                                self.logger.debug(
                                                    "id_{} 终点检测,三拼板,第一次检测,进行二次调整,前角:{:.2f},后角:{:.2f},第一块偏:{:.2f},第二块偏:{:.2f}, 非易断板".format(
                                                        self.cutterZmcIndex, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError))
                                            else:
                                                self.logger.debug(
                                                    "id_{} 终点检测,三拼板,第二次检测,进行二次调整,前角:{:.2f},后角:{:.2f},第二块偏:{:.2f},第三块偏:{:.2f} 非易断板".format(
                                                        self.cutterZmcIndex, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError))
                                            if self.finalCheckTimes == 1:
                                                cutterFinalAdjust = -firstBoardError
                                            else:
                                                cutterFinalAdjust = secondBoardError
                                            # 需要二次调整.直接将调整值写进去
                                            ret = self.cutterZmc.set_final_check_second_adjust_distance(cutterFinalAdjust)  # 告诉铡刀控制器需要移动的距离
                                            if ret != 0:
                                                self.keep_zmc_read_write_error('铡刀终点调整写入错误,通信异常', 'cutterZmc', self.cutterZmcIndex)
                                                return
                                            # 将需要二次调整的bit位也写进去
                                            ret = self.cutterZmc.set_final_check_second_adjust_flag()  # 验证失败,告诉底层进行报警操作
                                            if ret != 0:
                                                self.keep_zmc_read_write_error("set cutterZmc 28 failed .铡刀通信异常,请检查电源线和网线!", "cutter_zmc",
                                                                               self.cutterZmcIndex)
                                                return
                                            secondAdjust = True
                                            continue
                                        else:
                                            # 是二次检测依旧没有通过
                                            if self.finalCheckTimes == 1:
                                                self.logger.debug(
                                                    "id_{} 终点检测,三拼板,第一次检测,二次调整之后检测未通过,前角:{:.2f},后角:{:.2f},第一块偏:{:.2f},第二块偏:{:.2f} 非易断板".format(
                                                        self.cutterZmcIndex, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError))
                                            else:
                                                self.logger.debug(
                                                    "id_{} 终点检测,三拼板,第二次检测,二次调整之后检测未通过,前角:{:.2f},后角:{:.2f},第一块偏:{:.2f},第二块偏:{:.2f} 非易断板".format(
                                                        self.cutterZmcIndex, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError))
                                            secondAdjust = False
                                            ret = self.cutterZmc.set_final_check_result_fail()  # 验证失败,告诉底层进行报警操作
                                            if ret != 0:
                                                self.keep_zmc_read_write_error("set front_cutter 27 failed .铡刀通信异常,请检查电源线和网线!", "cutter_zmc",
                                                                               self.cutterZmcIndex)
                                                return
                                            self.finalCheckFlag = False
                                else:
                                    manualInfo = "人工确认" if self.staffConfirmed else "非人工确认"
                                    if self.staffConfirmed:
                                        self.staffConfirmed = False
                                    if self.finalCheckTimes == 1:
                                        self.logger.debug("id_{} 终点检测,三拼板,第一次检测,{}验证通过,前角:{:.2f},后角:{:.2f},第一块偏:{:.2f},第二块偏:{:.2f} 非易断板".format(
                                            self.cutterZmcIndex, manualInfo, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError))
                                        secondAdjust = False
                                        self.finalCheckTimes = 2
                                        self.finalCheckFlag = True
                                        # 将第二次需要移动的距离写入
                                        ret = self.cutterZmc.set_distance_3_pin_second_move(self.secondBoardLen)
                                        if ret != 0:
                                            self.keep_zmc_read_write_error("set cutterZmc modbus_ieee(126) failed", "cutter_zmc", self.cutterZmcIndex)
                                            return
                                    else:
                                        self.logger.debug("id_{} 终点检测,三拼板,第二次检测,{}验证通过,前角:{:.2f},后角:{:.2f},第一块偏:{:.2f},第二块偏:{:.2f} 非易断板".format(
                                            self.cutterZmcIndex, manualInfo, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError))
                                        secondAdjust = False
                                        self.finalCheckTimes = 1
                                        self.finalCheckFlag = False  # 跳出终点检测的循环
                                        # 跳出终点检测
                                    ret = self.cutterZmc.set_final_check_result_ok()
                                    if ret != 0:
                                        self.keep_zmc_read_write_error("set front_cutter 26 failed .铡刀控制器通信异常,请检查电源线和网线!", "cutter_zmc",
                                                                       self.cutterZmcIndex)
                                        return
                            else:
                                # 如果是易断板
                                if ((abs(frontAngleCheck) > self.finalFrontAngle and self.finalCheckTimes == 1) or
                                    (abs(firstBoardError) > self.finalFirstBoardThres and self.finalCheckTimes == 1) or
                                    (abs(frontAngleCheck) > self.finalThreeFrontAngle and self.finalCheckTimes == 2) or
                                    (abs(backAngleCheck) > self.finalThreeBackAngle and self.finalCheckTimes == 2) or
                                    (abs(secondBoardError) > self.finalSecondBoardThres and self.finalCheckTimes == 2)) and (not self.staffConfirmed):

                                    debugInfo = "id_{}  第 {} 次终点检测验证失败,前角:{:.2f},后角:{:.2f},第一块偏:{:.2f},第二块偏:{:.2f},两板相差:{:.2f}, 易断板".format(
                                        self.cutterZmcIndex, self.finalCheckTimes, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError,
                                        (firstBoardError + secondBoardError))
                                    self.logger.debug(debugInfo)
                                    if (abs(frontAngleCheck) > self.finalFrontAngle and self.finalCheckTimes == 1) or \
                                            (abs(backAngleCheck) > self.finalThreeBackAngle and self.finalCheckTimes == 2) or \
                                            (abs(frontAngleCheck) > self.finalThreeFrontAngle and self.finalCheckTimes == 2) or \
                                            ((firstBoardError + secondBoardError) > self.finalTotalBoardThres and self.finalCheckTimes == 2):

                                        self.logger.debug("id_{} 三拼板，第 {} 次终点检测: 验证出错,弹窗提示工人操作.".format(self.cutterZmcIndex, self.finalCheckTimes))
                                        if abs(frontAngleCheck) > self.finalFrontAngle:
                                            self.imageSend = imgRes2
                                        else:
                                            self.imageSend = imgRes1
                                        self.send_cutter_final_check_modal_to_web(self.imageSend, debugInfo)
                                        self.finalCheckModify = True
                                        while self.finalCheckModify:
                                            gevent.sleep(0.5)
                                            continue
                                        if self.staffConfirmed:
                                            continue
                                        gevent.sleep(0.5)
                                        continue
                                    else:
                                        if not secondAdjust:
                                            if self.finalCheckTimes == 1:
                                                self.logger.debug(
                                                    "id_{} 终点检测,三拼板,第一次检测,进行二次调整,前角:{:.2f},后角:{:.2f},第一块偏:{:.2f},第二块偏:{:.2f}, 易断板".format(
                                                        self.cutterZmcIndex, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError))
                                            else:
                                                self.logger.debug(
                                                    "id_{} 终点检测,三拼板,第二次检测,进行二次调整,前角:{:.2f},后角:{:.2f},第二块偏:{:.2f},第三块偏:{:.2f} 易断板".format(
                                                        self.cutterZmcIndex, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError))
                                            if self.finalCheckTimes == 1:
                                                cutterFinalAdjust = -firstBoardError
                                            else:
                                                cutterFinalAdjust = secondBoardError
                                            # 需要二次调整.直接将调整值写进去
                                            ret = self.cutterZmc.set_final_check_second_adjust_distance(cutterFinalAdjust)  # 告诉铡刀控制器需要移动的距离
                                            if ret != 0:
                                                self.keep_zmc_read_write_error('铡刀终点调整写入错误,通信异常', 'cutterZmc', self.cutterZmcIndex)
                                                return
                                            # 将需要二次调整的bit位也写进去
                                            ret = self.cutterZmc.set_final_check_second_adjust_flag()  # 验证失败,告诉底层进行报警操作
                                            if ret != 0:
                                                self.keep_zmc_read_write_error("set cutterZmc 28 failed .铡刀通信异常,请检查电源线和网线!", "cutter_zmc",
                                                                               self.cutterZmcIndex)
                                                return
                                            secondAdjust = True
                                            continue
                                        else:
                                            # 是二次检测依旧没有通过
                                            if self.finalCheckTimes == 1:
                                                self.logger.debug(
                                                    "id_{} 终点检测,三拼板,第一次检测,二次调整之后检测未通过,前角:{:.2f},后角:{:.2f},第一块偏:{:.2f},第二块偏:{:.2f} 易断板".format(
                                                        self.cutterZmcIndex, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError))
                                            else:
                                                self.logger.debug(
                                                    "id_{} 终点检测,三拼板,第二次检测,二次调整之后检测未通过,前角:{:.2f},后角:{:.2f},第一块偏:{:.2f},第二块偏:{:.2f} 易断板".format(
                                                        self.cutterZmcIndex, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError))
                                            secondAdjust = False
                                            ret = self.cutterZmc.set_final_check_result_fail()  # 验证失败,告诉底层进行报警操作
                                            if ret != 0:
                                                self.keep_zmc_read_write_error("set front_cutter 27 failed .铡刀通信异常,请检查电源线和网线!", "cutter_zmc",
                                                                               self.cutterZmcIndex)
                                                return
                                            self.finalCheckFlag = False
                                else:
                                    manualInfo = "人工确认" if self.staffConfirmed else "非人工确认"
                                    if self.staffConfirmed:
                                        self.staffConfirmed = False
                                    if self.finalCheckTimes == 1:
                                        self.logger.debug("id_{} 终点检测,三拼板,第一次检测,{}验证通过,前角:{:.2f},后角:{:.2f},第一块偏:{:.2f},第二块偏:{:.2f} 易断板".format(
                                            self.cutterZmcIndex, manualInfo, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError))
                                        secondAdjust = False
                                        self.finalCheckTimes = 2
                                        self.finalCheckFlag = True
                                        # 将第二次需要移动的距离写入
                                        ret = self.cutterZmc.set_distance_3_pin_second_move(self.secondBoardLen)
                                        if ret != 0:
                                            self.keep_zmc_read_write_error("set cutterZmc modbus_ieee(126) failed", "cutter_zmc", self.cutterZmcIndex)
                                            return
                                    else:
                                        self.logger.debug("id_{} 终点检测,三拼板,第二次检测,{}验证通过,前角:{:.2f},后角:{:.2f},第一块偏:{:.2f},第二块偏:{:.2f} 易断板".format(
                                            self.cutterZmcIndex, manualInfo, frontAngleCheck, backAngleCheck, firstBoardError, secondBoardError))
                                        secondAdjust = False
                                        self.finalCheckTimes = 1
                                        self.finalCheckFlag = False  # 跳出终点检测的循环
                                        # 跳出终点检测
                                    ret = self.cutterZmc.set_final_check_result_ok()
                                    if ret != 0:
                                        self.keep_zmc_read_write_error("set front_cutter 26 failed .铡刀控制器通信异常,请检查电源线和网线!", "cutter_zmc",
                                                                       self.cutterZmcIndex)
                                        return
                    else:
                        gevent.sleep(0.3)
                        continue

            else:
                gevent.sleep(0.3)
                continue

    def web_request_cutter_begin_check_again(self):
        """
        首检弹框那里,界面上点击了重新检测
        :return:
        """
        self.logger.debug("id_{} 起点检测: 界面上点击了重新检测按钮".format(self.cutterZmcIndex))
        self.cutterZmc.open_door_alarm()  # 免得某个地方又忘记关了.
        ret = self.cutterZmc.set_allow_begin_check_again_flag()
        gevent.sleep(0.3)
        self.beginCheckModify = False
        if ret != 0:
            self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
            return
        return "ok"

    def web_request_cutter_final_cut_board(self):
        """
        前端界面上点击了人工去人铡板
        :return:
        """
        resVal1, resVal2 = self.cutterZmc.get_door_start()
        if resVal1 != 1 or resVal2 != 1:
            debugInfo = "铡刀防护门未关闭， 请确认门关闭之后，重新点击 人工确认铡板"
            self.logger.debug("id_{} 两拼板，终点检测: 界面上点击了人工确认铡板,检测到门没有关闭,弹窗提示工人关门!".format(self.cutterZmcIndex))
            self.send_cutter_final_check_modal_to_web(self.imageSend, debugInfo, fontStart=True)
        else:
            self.logger.debug("id_{} {}拼板，终点检测: 界面上点击了人工确认铡板,重新拍照并执行分板动作".format(self.cutterZmcIndex, self.jointNumber))
            self.cutterZmc.open_door_alarm()
            gevent.sleep(0.3)
            self.finalCheckModify = False
            ret = self.cutterZmc.set_allow_final_check_again_flag()
            self.staffConfirmed = True
            if ret != 0:
                self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
                return
            return "ok"

    def web_request_cutter_final_check_again(self):
        """
        终检弹框那里,界面上点击了重新检测,终检这里重新检测一次
        :return:
        """
        self.logger.debug("id_{} 两拼板，终点检测: 界面上点击了重新检测按钮".format(self.cutterZmcIndex))
        self.cutterZmc.open_door_alarm()
        ret = self.cutterZmc.set_allow_final_check_again_flag()
        gevent.sleep(0.3)
        self.finalCheckModify = False
        if ret != 0:
            self.keep_zmc_read_write_error(ret, "cutterZmc", self.cutterZmcIndex)
            return
        return "ok"

    def send_cutter_begin_check_modal_to_web(self, imageSend, info):
        """
        发送首检出问题的提示框
        :param imageSend:
        :param info:
        :return:
        """
        msgVal = {
            "val": "beginCheckError",
            "cutterImageSrc": common_tools.image_to_string(imageSend),
            "cutterHintInfo": info
        }
        self.cutterZmc.close_door_alarm()
        send_produce_state_to_ui(msgVal)

    def send_cutter_final_check_modal_to_web(self, imageSend, info, fontStart=False):
        """
        发送终检出问题的提示框
        :param fontStart:
        :param imageSend:
        :param info:
        :return:
        """
        msgVal = {
            "val": "finalCheckError",
            "cutterImageSrc": common_tools.image_to_string(imageSend),
            "cutterHintInfo": info,
            "fontStart": fontStart
        }
        self.cutterZmc.close_door_alarm()
        send_produce_state_to_ui(msgVal)

    def cbj_zmc_task(self):
        """
        发送切线的任务函数
        :return:
        """
        self.logger.debug("裁切机发送切线的任务函数开启")
        lastDragTime = 0
        while True:
            if self.deviceStates.workStopFlag:
                self.logger.debug("id_{} workStopFlag = True, 裁切机发送切线的任务函数退出".format(self.cbjZmcIndex))
                return
            ret, val = self.cbjZmc.get_allow_send_lines()
            if ret != 0:
                self.keep_zmc_read_write_error(ret, "cbjZmc", self.cbjZmcIndex)
                return
            if val == 1:
                res, knifeCutParams = self.db.get_knife_cut_params()
                if res != "ok":
                    errorInfo = knifeCutParams
                    print(errorInfo)
                    self.logger.debug(errorInfo)
                    self.cutDeep = 0
                else:
                    self.cutDeep = knifeCutParams.get("cutDeep")
                # 收到发送切线的函数,开始发送切线
                if not self.linesQueue.empty():
                    boardLine = self.linesQueue.get()
                    self.cbjZmcIndex = boardLine["cutterIndex"]
                    newData = {
                        "cbjZmcIndex": self.cbjZmcIndex
                    }
                    self.update_working_info_to_db_and_web(newData)

                    linesSend = boardLine["lines"]
                    cutSpeed = boardLine["cutSpeed"]
                    firstPointX = boardLine["firstPointX"]
                    if self.jointNumber == "2":
                        boardLength = boardLine["topToBottom"]
                    elif self.jointNumber == "3":
                        boardLength = boardLine["firstBoardLen"] + boardLine["secondBoardLen"] + boardLine["thirdBoardLen"]
                    else:
                        boardLength = boardLine["singleBoardWidth"]

                    boardWidth = boardLine["cutWidth"]
                    self.logger.debug("id_{} 裁切机: get cbj modbus(25) = 1,收到发送切线信号,发送切线:{}".format(self.cbjZmcIndex, linesSend))
                    # 先将第一个点的坐标和板的长度发送到底层
                    ret = self.cbjZmc.set_table(87, 2, [firstPointX, boardLength])
                    if ret != 0:
                        self.keep_zmc_read_write_error(ret, "cbjZmc", self.cbjZmcIndex)
                        return
                    # 将是否切第三刀发送到底层
                    if not self.isThirdCut:
                        thirdCutNeeded = False
                    else:
                        thirdCutNeeded = True

                    self.cbjZmc.set_third_cut_needed(thirdCutNeeded)
                    # 获取最后一次反馈回来的坐标
                    self.logger.debug("id_{} 裁切机: 获取上一次切线反馈回来的坐标值".format(self.cbjZmcIndex))
                    res, lastData = self.cbjZmc.get_table(10000, 16)
                    for i in range(0, 16, 4):
                        self.logger.debug("id_{} 第 {} 条切线:({:.2f},{:.2f}),({:.2f},{:.2f})".format(
                            self.cbjZmcIndex, i // 4 + 1, lastData[i], lastData[i + 1], lastData[i + 2], lastData[i + 3]))
                    self.cutterZmc.set_cut_board_width(boardWidth / 2)
                    self.cutterZmc.set_cut_board_width_finish()
                    self.logger.debug("id_{} 裁切机: 设置板宽和允许吹气完成,设置的板宽的一半:{:.2f}".format(self.cbjZmcIndex, boardWidth / 2))
                    lineList = []  # 要发送的切线
                    pointsNumber = len(linesSend)
                    for i in range(0, pointsNumber, 2):
                        lineList.append([linesSend[i], linesSend[i + 1]])
                    # 获取第一刀的切割速度
                    speedList = []
                    for i in range(len(lineList)):
                        if i == 0:
                            speedList.append(self.firstCutSpeed)
                        else:
                            speedList.append(cutSpeed)
                    res, info = self.cbjZmc.set_lines_to_cbj_with_capfile(lineList, speedList, self.cutDeep, self.cbjZmcIndex)
                    if res != "ok":
                        self.logger.debug("id_{} 裁切机: 切线发送失败: {}".format(self.cbjZmcIndex, info))
                        self.keep_zmc_read_write_error(info, "cbjZmc", self.cbjZmcIndex)
                        return
                    else:
                        ret = self.cbjZmc.set_cut_lines_send_finish()
                        if ret != 0:
                            self.keep_zmc_read_write_error(ret, "cbjZmc", self.cbjZmcIndex)
                            return
                        self.logger.debug("id_{} 裁切机: 设置裁切机控制器切线发送完毕.".format(self.cbjZmcIndex))
                    # 等待拖拽
                    while True:
                        if self.deviceStates.workStopFlag:
                            self.logger.debug("id_{} workStopFlag = True, 裁切机发送切线的任务函数退出".format(self.cbjZmcIndex))
                            return
                        ret, val = self.cbjZmc.get_drag_begin_state()
                        if ret != 0:
                            self.keep_zmc_read_write_error(ret, "cbjZmc", self.cbjZmcIndex)
                            return
                        if val == 1:
                            now = time.time()
                            if lastDragTime == 0:
                                dragBetween = 0
                                lastDragTime = now
                            else:
                                dragBetween = round(now - lastDragTime, 2)
                                lastDragTime = now
                            self.logger.debug("id_{} 裁切机: 收到拖拽开始的信号,两次拖拽间隔:{}".format(self.cbjZmcIndex, dragBetween))
                            boardKey = boardLine["key"]
                            if self.jointNumber == "2":
                                self.cutterZmcIndex = boardLine["cutterIndex"]
                                self.topToBottom = boardLine["topToBottom"]
                                self.topToMiddle = boardLine["topToMiddle"]
                                self.topMidAngle = boardLine["topMiddleAngle"]
                                self.bottomMidAngle = boardLine["bottomMiddleAngle"]
                                self.logger.debug("id_{} 裁切机: 铡刀数据更新完成,第一块:{:.2f},角度:{:.2f};第二块:{:.2f},角度:{:.2f}".format(
                                    self.cbjZmcIndex, self.topToMiddle, self.topMidAngle, self.topToBottom - self.topToMiddle, self.bottomMidAngle))
                            elif self.jointNumber == "3":
                                self.cutterZmcIndex = boardLine["cutterIndex"]
                                self.firstBoardLen = boardLine["firstBoardLen"]
                                self.secondBoardLen = boardLine["secondBoardLen"]
                                self.thirdBoardLen = boardLine["thirdBoardLen"]
                                self.topFirstMiddleAngle = boardLine["topFirstMiddleAngle"]
                                self.bottomFirstMiddleAngle = boardLine["bottomFirstMiddleAngle"]
                                self.twoMiddleAngle = boardLine["twoMiddleAngle"]
                                self.bottomSecondMiddleAngle = boardLine["bottomSecondMiddleAngle"]

                                self.topMidAngle = self.topFirstMiddleAngle
                                self.bottomMidAngle = self.bottomSecondMiddleAngle
                                self.topToMiddle = self.firstBoardLen
                                self.logger.debug(
                                    "id_{} 铡刀数据更新完成,第一块={:.2f},第二块={:.2f},第三块={:.2f},topFirstMid={:.2f},bottomFirstMid={:.2f},twoMiddle={:.2f},bottomMiddle={:.2f}".format(
                                        self.cbjZmcIndex, self.firstBoardLen, self.secondBoardLen, self.thirdBoardLen, self.topFirstMiddleAngle,
                                        self.bottomFirstMiddleAngle,
                                        self.twoMiddleAngle, self.bottomSecondMiddleAngle))
                            ret = self.cbjZmc.set_update_cutter_state_finish()
                            if ret != 0:
                                self.keep_zmc_read_write_error(ret, "cbjZmc", self.cbjZmcIndex)
                                return
                            else:
                                self.update_cut_info(boardKey, int(self.jointNumber), float(boardLine["cutDis"] / 1000),
                                                     float(boardLine["cutArea"] / 10000), float(dragBetween / 60 / 60 / 24))
                                break
                        else:
                            gevent.sleep(0.05)
                            continue
                else:
                    gevent.sleep(0.5)
                    self.logger.debug("id_{} 裁切机: 收到发送切线的函数,切线队列为空".format(self.cbjZmcIndex))
            else:
                gevent.sleep(0.05)

    def cam_zmc_task(self):
        """
        上料监控任务开启,主要负责上料拍照以及和底层通信
        :return:
        """
        self.logger.debug("上料拍照任务函数开启.")
        imageFailedCount = 0
        self.firstCheckBoardLen1 = 0
        self.firstCheckBoardLen2 = 0
        self.disByLineCount = 0
        while True:
            if self.deviceStates.workStopFlag:
                self.logger.debug("id_{} workStopFlag = True, 上料拍照任务退出.".format(self.camZmcIndex))
                return
            if not self.isAllowGetImage:
                gevent.sleep(0.5)
                continue
            if self.waitBoardBack:
                gevent.sleep(0.5)
                continue

            # 1.等待允许拍照的信号
            ret, val = self.camZmc.get_allow_get_pic_state()
            if ret != 0:
                self.keep_zmc_read_write_error(ret, "camZmc", self.camZmcIndex)
            if val == 1:
                self.logger.debug("id_{} 上料: get modbusbit(15) = 1,准备拍照!".format(self.camZmcIndex))
                info = ""
                self.b = None
                self.b = {
                    "cutSpeed": self.cutSpeed
                }
                res, img = self.imageJoint.get_image_from_line_cam()
                if res != "ok":
                    self.lineImage = None
                    # 2.如果这里已经抓图图片失败了,就没有必要往前走了,直接写入日志,并进行重拍.
                    if imageFailedCount == 0:
                        self.logger.debug("id_{} 上料: 相机获取图片失败1次,重新打开相机并进行重拍: {}".format(self.camZmcIndex, res))
                        ret = self.camZmc.img_result_failed()
                        if ret != "0":
                            self.keep_zmc_read_write_error(ret, "camZmc", self.camZmcIndex)
                            return
                        self.imageJoint.re_start_line_camera()
                        imageFailedCount += 1
                        continue
                    else:
                        self.logger.debug("id_{} 上料: 相机获取失败2次,前端界面弹窗提示: {}".format(self.camZmcIndex, res))
                        imageFailedCount = 0
                        self.keep_zmc_read_write_error(res, "cameraError", self.camZmcIndex)
                        continue
                else:
                    self.lineImage = img
                    self.logger.debug("id_{} 上料: 相机获取图片OK".format(self.camZmcIndex))
                    imageFailedCount = 0
                    keepTime = time.strftime('%Y-%m-%d_%H-%M-%S', time.localtime())
                    self.fileName = "{}--{}--w_{}--h_{}".format(self.camZmcIndex, keepTime, self.widthPix, self.heightPix)
                    # 3.如果是两拼板,先去检测物料的区域是否ok.
                    totalBoardRes = check_total_board_is_ok(self.lineImage, self.totalHeightMax, self.totalHeightMin, self.boardBreak,
                                                            self.jointNumber)
                    checkRes, hintInfo, totalBoardBox, totalBoardNoAngle, totalBoardOffset, \
                        totalBoardThresWithAngle, totalBoardResWithAngle = totalBoardRes
                    self.totalBoardBox = totalBoardBox
                    self.totalBoardThresWithAngle = totalBoardThresWithAngle
                    self.totalBoard = totalBoardNoAngle
                    self.totalBoardOffset = totalBoardOffset
                    # 物料检测通过
                    if checkRes == "ok":
                        self.logger.debug("id_{} 上料: {}拼板,物料检测通过: {}".format(self.camZmcIndex, self.jointNumber, hintInfo))
                    elif (checkRes == "noBoard" or checkRes == "sensorPosError") and self.disByLineCount < 1:
                        self.logger.debug("id_{} 上料: {}拼板,物料检测未通过: {},自动重拍".format(self.camZmcIndex, self.jointNumber, hintInfo))
                        save_totalboard_error_image(self.camZmcIndex, self.lineImage, totalBoardResWithAngle, self.jointNumber)
                        self.re_get_image()
                        self.disByLineCount += 1
                        continue
                    else:
                        # 物料检测未通过
                        self.logger.debug("id_{} 上料: {}拼板,物料检测未通过: {}".format(self.camZmcIndex, self.jointNumber, hintInfo))
                        # 未通过之后,就要弹窗提示工人,人工确认,将未通过的图片保存起来
                        save_totalboard_error_image(self.camZmcIndex, self.lineImage, totalBoardResWithAngle, self.jointNumber)
                        totalBoardSend = self.get_web_show_image(totalBoardResWithAngle, totalBoardCheckImage=True)
                        msgVal = {
                            "val": "totalBoardError",
                            "hintInfo": "id_{} 上料: {}拼板, 物料检测未通过: {}".format(self.camZmcIndex, self.jointNumber, hintInfo),
                            "totalBoardRes": totalBoardSend
                        }
                        send_produce_state_to_ui(msgVal)
                        # 等待界面上弹窗确认,怎么搞,是直接通过,还是重新拍照.
                        self.webModify = True  # 等待前端界面工人操作.
                        self.totalBoardIsOk = False  # 物料是不OK的,如果工单确认了OK,就直接进行下面的操作.
                        while self.webModify and not self.deviceStates.workStopFlag:
                            gevent.sleep(0.5)
                            continue
                        # 这里的弹窗,界面要显示几个按钮,板回退,停止,重新拍照,物料OK.
                        if not self.totalBoardIsOk:  # 如果没有点击物料OK,就继续循环进行检测,无论是点了板回退,还是重新拍照都一样
                            continue
                    self.disByLineCount = 0
                    # 如果是第一块板
                    if self.isFirstBoard:
                        if self.jointNumber == "2":
                            resFirst, boardSizeList, boardResList, upBoard = get_first_check_result(self.lineImage, self.widthPix, self.heightPix,
                                                                                                    self.calcThresBig, self.totalBoardBox,
                                                                                                    self.isFanzhuan)
                            self.upBoard = upBoard
                            if resFirst == "ok":
                                self.boardSizeList = boardSizeList
                                self.boardResList = boardResList
                                self.sendIndex = 0
                                boardSend = self.get_web_show_image(self.boardResList[self.sendIndex])
                                msgVal = {
                                    "val": "boardSizeCheck",
                                    "boardRes": boardSend,
                                    "boardSize": self.boardSizeList[self.sendIndex],
                                    "inputSize": (self.widthMm, self.heightMm),
                                    "boardCalcError": (round(self.boardSizeList[self.sendIndex][0] - self.widthMm, 2),
                                                       round(self.boardSizeList[self.sendIndex][1] - self.heightMm, 2))  # 误差
                                }
                                send_produce_state_to_ui(msgVal)
                                self.waitCheckBoardSize = True
                                self.sizeCheckOkBtnClick = False
                                while self.waitCheckBoardSize:
                                    if self.sizeCheckOkBtnClick:
                                        self.waitCheckBoardSize = False
                                    gevent.sleep(0.1)
                            # 进行人工标注尺寸的逻辑,handSize 尺寸的缩放比是前端发送过来的
                            else:
                                self.web_request_hand_inner_board_size()  # 弹窗人工标注内板尺寸框,这个时候也要灯盖前端人工标注.
                                self.waitHandLabelInnerBoard = True
                                while self.waitHandLabelInnerBoard:
                                    gevent.sleep(0.1)

                            # 首板获取两个框.算法其实都差不多,就是第一片没有位置信息
                            res, info, upBox, downBox, totalBoard, totalBoardThres = get_boxes_first(self.lineImage, self.widthPix, self.heightPix,
                                                                                                     self.calcThresBig, self.calcThresSmall,
                                                                                                     self.totalBoardBox, self.totalBoardThresWithAngle,
                                                                                                     self.isFanzhuan)
                            if res:
                                # 如果识别成功:
                                self.upBox = upBox
                                self.downBox = downBox
                                assert upBox is not None
                                assert downBox is not None
                                cutLinesRes = get_lines_with_boxes(self.lineImage, self.totalBoard, self.totalBoardBox, self.totalBoardThresWithAngle,
                                                                   self.upBox, self.downBox, self.transMtx, self.topPix, self.leftPix,
                                                                   self.bottomPix, self.rightPix, self.totalBoardOffset)

                                self.cutLines, self.imgCut, self.cutDis, self.isFirstCut, self.isThirdCut, self.cutWidth = cutLinesRes
                                self.logger.debug("id_{} 上料: 首板,识别成功,切线:{}".format(self.camZmcIndex, self.cutLines[:-4]))
                                for point in self.cutLines[:-4]:
                                    if point[0] > self.cbjLimitX or point[0] < 0 or point[1] > self.cbjLimitY or point[1] < 0:
                                        res = "lineError"
                                        info = "切线超限,请确认裁切机X和Y的极限值是否正确!xLimit={},yLimit={}".format(self.cbjLimitX, self.cbjLimitY)
                                        self.logger.debug("id_{} 上料: {}".format(self.camZmcIndex, info))
                                        break

                        elif self.jointNumber == "3":
                            resFirstCheck, boardSizesList, boardResList, upBoard = get_size_check_result_3p(self.lineImage, self.widthPix,
                                                                                                            self.heightPix, self.calcThresBig,
                                                                                                            self.totalBoardBox, self.totalBoard)

                            if resFirstCheck == "ok":
                                self.boardSizeList = boardSizesList
                                self.boardResList = boardResList
                                self.sendIndex = 0
                                boardSend = self.get_web_show_image(self.boardResList[self.sendIndex])
                                msgVal = {
                                    "val": "boardSizeCheck",
                                    "boardRes": boardSend,
                                    "boardSize": self.boardSizeList[self.sendIndex],
                                    "inputSize": (self.widthMm, self.heightMm),
                                    "boardCalcError": (round(self.boardSizeList[self.sendIndex][0] - self.widthMm, 2),
                                                       round(self.boardSizeList[self.sendIndex][1] - self.heightMm, 2))  # 误差
                                }
                                send_produce_state_to_ui(msgVal)
                                self.waitCheckBoardSize = True
                                self.sizeCheckOkBtnClick = False
                                while self.waitCheckBoardSize:
                                    if self.sizeCheckOkBtnClick:
                                        self.waitCheckBoardSize = False
                                    gevent.sleep(0.1)
                                try:
                                    if self.jointNumber == "3":
                                        res, info, boxesRes = get_boxes_3p(self.lineImage, self.widthPix, self.heightPix, self.calcThresBig,
                                                                           self.calcThresSmall, self.totalBoardBox, self.totalBoard,
                                                                           self.upLineOffset, self.middleLineOffset, self.downLineOffset,
                                                                           self.isFirstBoard)

                                        self.boxesRes = boxesRes
                                        if res:
                                            self.upBox = boxesRes[0]
                                            self.middleBox = boxesRes[1]
                                            self.downBox = boxesRes[2]
                                            assert self.upBox is not None
                                            assert self.middleBox is not None
                                            assert self.downBox is not None
                                            cutLinesRes = get_lines_with_boxes_3p(self.lineImage, self.totalBoard, self.totalBoardBox,
                                                                                  self.totalBoardThresWithAngle, self.boxesRes, self.transMtx,
                                                                                  self.topPix, self.leftPix, self.bottomPix,
                                                                                  self.rightPix, self.totalBoardOffset)
                                            self.cutLines, self.imgCut, self.cutDis, self.isFirstCut, self.isThirdCut, self.cutWidth = cutLinesRes
                                            self.logger.debug("id_{} 留边宽度:{} 识别的切线结果: {}".format(
                                                self.camZmcIndex, [self.topMm, self.leftMm, self.bottomMm, self.rightMm], self.cutLines))
                                        if type(self.cutLines) == type([]):
                                            for point in self.cutLines[:-4]:
                                                if point[0] > self.cbjLimitX or point[0] < 0 or point[1] > self.cbjLimitY or point[1] < 0:
                                                    res = "lineError"
                                                    info = "切线超限,请确认裁切机X和Y的极限值是否正确!xLimit={},yLimit={}".format(self.cbjLimitX, self.cbjLimitY)
                                                    self.logger.debug("id_{} 上料: {}".format(self.camZmcIndex, info))
                                                    break
                                except Exception as e:
                                    self.logger.debug("id_{}  calc error:{}".format(self.camZmcIndex, str(e)))
                                    self.imgCut = None
                        else:
                            resFirst, boardSizeList, boardResList, upBoard = get_size_check_result_1p(self.lineImage, self.widthPix, self.heightPix,
                                                                                                      self.calcThresBig, self.totalBoardBox,
                                                                                                      self.totalBoard)
                            self.upBoard = upBoard
                            if resFirst == "ok":
                                self.boardSizeList = boardSizeList
                                self.boardResList = boardResList
                                self.sendIndex = 0
                                boardSend = self.get_web_show_image(self.boardResList[self.sendIndex])
                                msgVal = {
                                    "val": "boardSizeCheck",
                                    "boardRes": boardSend,
                                    "boardSize": self.boardSizeList[self.sendIndex],
                                    "lastBoardSize": (self.widthMm, self.heightMm),
                                    "inputSize": (self.orderWidthMm, self.orderHeightMm),
                                    "boardCalcError": (round(self.boardSizeList[self.sendIndex][0] - self.widthMm, 2),
                                                       round(self.boardSizeList[self.sendIndex][1] - self.heightMm, 2))  # 误差
                                }
                                send_produce_state_to_ui(msgVal)
                                self.waitCheckBoardSize = True
                                self.sizeCheckOkBtnClick = False
                                while self.waitCheckBoardSize:
                                    if self.sizeCheckOkBtnClick:
                                        self.waitCheckBoardSize = False
                                    gevent.sleep(0.1)
                            # 进行人工标注尺寸的逻辑,handSize 尺寸的缩放比是前端发送过来的
                            else:
                                self.web_request_hand_inner_board_size()  # 弹窗人工标注内板尺寸框,这个时候也要灯盖前端人工标注.
                                self.waitHandLabelInnerBoard = True
                                while self.waitHandLabelInnerBoard:
                                    gevent.sleep(0.1)

                            res, info, singleBox = get_box_1pin(self.lineImage, self.widthPix, self.heightPix, self.calcThresBig, self.calcThresSmall,
                                                                self.totalBoardBox, self.totalBoard, self.singBoardOffset, self.isFirstBoard)
                            if res:
                                self.singleBox = singleBox
                                cutLinesRes = get_lines_with_boxes_1p(self.lineImage, self.totalBoard, self.totalBoardBox, self.totalBoardThresWithAngle,
                                                                      self.singleBox, self.transMtx, self.topPix, self.leftPix, self.bottomPix,
                                                                      self.rightPix, self.totalBoardOffset)
                                self.cutLines, self.imgCut, self.isFirstCut, self.isThirdCut, \
                                    self.singleBoardLen, self.cutWidth, self.cutDis = cutLinesRes
                                if self.isFirstBoard:
                                    self.logger.debug("id_{} 上料: 首板,单拼板,识别成功,切线:{}".format(self.camZmcIndex, self.cutLines[:-4]))
                                else:
                                    self.logger.debug("id_{} 上料: 非首板,单拼板,识别成功,切线:{}".format(self.camZmcIndex, self.cutLines[:-4]))

                                for point in self.cutLines[:-4]:
                                    if point[0] > self.cbjLimitX or point[0] < 0 or point[1] > self.cbjLimitY or point[1] < 0:
                                        res = "lineError"
                                        info = "切线超限,请确认裁切机X和Y的极限值是否正确!xLimit={},yLimit={}".format(self.cbjLimitX, self.cbjLimitY)
                                        self.logger.debug("id_{} 上料: {}".format(self.camZmcIndex, info))
                                        break

                    else:
                        if self.jointNumber == "2":
                            res, info, upBox, downBox, totalBoard, totalBoardThres = get_boxes(self.lineImage, self.widthPix,
                                                                                               self.heightPix, self.calcThresBig,
                                                                                               self.calcThresSmall, self.innerToEdgeThres,
                                                                                               self.totalBoardBox, self.totalBoardThresWithAngle,
                                                                                               self.upLineOffset, self.downLineOffset,
                                                                                               self.isFanzhuan)
                            if res:
                                # 如果识别成功:
                                self.upBox = upBox
                                self.downBox = downBox
                                assert upBox is not None
                                assert downBox is not None
                                cutLinesRes = get_lines_with_boxes(self.lineImage, self.totalBoard, self.totalBoardBox, self.totalBoardThresWithAngle,
                                                                   self.upBox, self.downBox, self.transMtx, self.topPix, self.leftPix,
                                                                   self.bottomPix, self.rightPix, self.totalBoardOffset)

                                self.cutLines, self.imgCut, self.cutDis, self.isFirstCut, self.isThirdCut, self.cutWidth = cutLinesRes
                                if self.isFirstBoard:
                                    self.logger.debug("id_{} 上料: 首板,识别成功,切线:{}".format(self.camZmcIndex, self.cutLines[:-4]))
                                else:
                                    self.logger.debug("id_{} 上料: 非首板,识别成功,切线:{}".format(self.camZmcIndex, self.cutLines[:-4]))

                                for point in self.cutLines[:-4]:
                                    if point[0] > self.cbjLimitX or point[0] < 0 or point[1] > self.cbjLimitY or point[1] < 0:
                                        res = "lineError"
                                        info = "切线超限,请确认裁切机X和Y的极限值是否正确!xLimit={},yLimit={}".format(self.cbjLimitX, self.cbjLimitY)
                                        self.logger.debug("id_{} 上料: {}".format(self.camZmcIndex, info))
                                        break

                        elif self.jointNumber == "3":
                            res, info, boxesRes = get_boxes_3p(self.lineImage, self.widthPix, self.heightPix, self.calcThresBig,
                                                               self.calcThresSmall, self.totalBoardBox, self.totalBoard,
                                                               self.upLineOffset, self.middleLineOffset, self.downLineOffset,
                                                               self.isFirstBoard)

                            self.boxesRes = boxesRes
                            if res:
                                self.upBox = boxesRes[0]
                                self.middleBox = boxesRes[1]
                                self.downBox = boxesRes[2]
                                assert self.upBox is not None
                                assert self.middleBox is not None
                                assert self.downBox is not None
                                cutLinesRes = get_lines_with_boxes_3p(self.lineImage, self.totalBoard, self.totalBoardBox,
                                                                      self.totalBoardThresWithAngle, self.boxesRes, self.transMtx,
                                                                      self.topPix, self.leftPix, self.bottomPix,
                                                                      self.rightPix, self.totalBoardOffset)
                                self.cutLines, self.imgCut, self.cutDis, self.isFirstCut, self.isThirdCut, self.cutWidth = cutLinesRes
                                self.logger.debug("id_{} 留边宽度:{} 识别的切线结果: {}".format(
                                    self.camZmcIndex, [self.topMm, self.leftMm, self.bottomMm, self.rightMm], self.cutLines))

                            for point in self.cutLines[:-4]:
                                if point[0] > self.cbjLimitX or point[0] < 0 or point[1] > self.cbjLimitY or point[1] < 0:
                                    res = "lineError"
                                    info = "切线超限,请确认裁切机X和Y的极限值是否正确!xLimit={},yLimit={}".format(self.cbjLimitX, self.cbjLimitY)
                                    self.logger.debug("id_{} 上料: {}".format(self.camZmcIndex, info))
                                    break

                        else:
                            res, info, singleBox = get_box_1pin(self.lineImage, self.widthPix, self.heightPix, self.calcThresBig, self.calcThresSmall,
                                                                self.totalBoardBox, self.totalBoard, self.singBoardOffset, self.isFirstBoard)
                            if res:
                                self.singleBox = singleBox
                                cutLinesRes = get_lines_with_boxes_1p(self.lineImage, self.totalBoard, self.totalBoardBox, self.totalBoardThresWithAngle,
                                                                      self.singleBox, self.transMtx, self.topPix, self.leftPix, self.bottomPix,
                                                                      self.rightPix, self.totalBoardOffset)
                                self.cutLines, self.imgCut, self.isFirstCut, self.isThirdCut, \
                                    self.singleBoardLen, self.cutWidth, self.cutDis = cutLinesRes
                                if self.isFirstBoard:
                                    self.logger.debug("id_{} 上料: 首板,单拼板,识别成功,切线:{}".format(self.camZmcIndex, self.cutLines[:-4]))
                                else:
                                    self.logger.debug("id_{} 上料: 非首板,单拼板,识别成功,切线:{}".format(self.camZmcIndex, self.cutLines[:-4]))

                                for point in self.cutLines[:-4]:
                                    if point[0] > self.cbjLimitX or point[0] < 0 or point[1] > self.cbjLimitY or point[1] < 0:
                                        res = "lineError"
                                        info = "切线超限,请确认裁切机X和Y的极限值是否正确!xLimit={},yLimit={}".format(self.cbjLimitX, self.cbjLimitY)
                                        self.logger.debug("id_{} 上料: {}".format(self.camZmcIndex, info))
                                        break

                    if not res or res == "lineError":
                        if res == "lineError":
                            imageSendLineError = self.get_web_show_image(self.imgCut)
                            msgVal = {
                                "val": "cutLineCheck",
                                "cutImage": imageSendLineError,
                                "cutInfo": info
                            }
                            if self.jointNumber == '2':
                                save_cut_line_image(self.camZmcIndex, self.imgCut, self.widthPix, self.heightPix, endwith="calc",
                                                    isFanzhuan=self.isFanzhuan)
                            elif self.jointNumber == "3":
                                save_three_cut_line_image(self.camZmcIndex, self.imgCut, self.widthPix, self.heightPix, endwith="calc",
                                                          isFanzhuan=self.isFanzhuan)
                            else:
                                save_one_cut_line_image(self.camZmcIndex, self.imgCut, self.widthPix, self.heightPix, endwith="calc",
                                                        isFanzhuan=self.isFanzhuan)
                        else:
                            imageSend = self.get_web_show_image(self.totalBoard)
                            # 计算缩放比例
                            labelSizePix = [self.widthPix // cp.sizeScale, self.heightPix // cp.sizeScale]
                            labelSizeMm = [self.widthMm, self.heightMm]
                            msgVal = {
                                "val": "handAnnotated",
                                "totalBoard": imageSend,
                                "labelSizeMm": labelSizeMm,
                                "labelSizePix": labelSizePix,
                                "cutInfo": info
                            }
                        self.webModify = True
                        send_produce_state_to_ui(msgVal)
                        if self.jointNumber == '2':
                            save_calc_error_image(self.camZmcIndex, self.lineImage, self.widthPix, self.heightPix, self.upLineOffset,
                                                  self.downLineOffset, self.isFanzhuan)
                        elif self.jointNumber == "3":
                            save_three_calc_error_image(self.camZmcIndex, self.lineImage, self.widthPix, self.heightPix, self.upLineOffset,
                                                        self.downLineOffset, self.isFanzhuan)
                        else:
                            save_one_calc_error_image(self.camZmcIndex, self.lineImage, self.widthPix, self.heightPix, self.upLineOffset,
                                                      self.downLineOffset, self.isFanzhuan)

                    else:
                        if self.jointNumber == '2':
                            save_calc_ok_image(self.camZmcIndex, self.lineImage, self.widthPix, self.heightPix, self.upLineOffset,
                                               self.downLineOffset, self.isFanzhuan)
                        elif self.jointNumber == "3":
                            save_three_calc_ok_image(self.camZmcIndex, self.lineImage, self.widthPix, self.heightPix, self.upLineOffset,
                                                     self.downLineOffset, self.isFanzhuan)
                        else:
                            save_one_calc_error_image(self.camZmcIndex, self.lineImage, self.widthPix, self.heightPix, self.upLineOffset,
                                                      self.downLineOffset, self.isFanzhuan)
                        if self.isFirstBoard:
                            self.webModify = True
                            imageSend = self.get_web_show_image(self.imgCut)
                            msgVal = {
                                "val": "cutLineCheck",
                                "cutImage": imageSend,
                                "cutInfo": info
                            }
                            send_produce_state_to_ui(msgVal)
                        else:
                            if self.jointNumber == "2":
                                firstHeight = self.cutLines[-4]  # 第一块板长
                                secondHeight = self.cutLines[-3] - self.cutLines[-4]  # 第二块板长
                                totalHeight = self.cutLines[-3]
                                firstBoardError = firstHeight - self.firstCheckBoardLen1
                                secondBoardError = secondHeight - self.firstCheckBoardLen2
                                totalBoardError = totalHeight - self.firstCheckBoardLen1 - self.firstCheckBoardLen2
                                if abs(firstBoardError) > self.firstBoardErrorThres or abs(secondBoardError) > self.secondBoardErrorThres or abs(
                                        totalBoardError) > self.totalBoardErrorThres:
                                    info = 'id_{} 上料：和首板尺寸验证失败,弹窗提示确认切线是否OK.偏差值,第一块={:.2f},第二块={:.2f},总长偏差={:.2f}'.format(
                                        self.camZmcIndex, firstBoardError, secondBoardError, totalBoardError)
                                    self.logger.debug(info)
                                    imageSend = self.get_web_show_image(self.imgCut)
                                    msgVal = {
                                        "val": "cutLineCheck",
                                        "cutImage": imageSend,
                                        "cutInfo": info
                                    }
                                    send_produce_state_to_ui(msgVal)
                                    self.webModify = True
                                else:
                                    self.logger.debug('id_{} 上料：和首板尺寸验证通过.偏差值,第一块={:.2f},第二块={:.2f},总长偏差={:.2f}'.format(
                                        self.camZmcIndex, firstBoardError, secondBoardError, totalBoardError))
                                    self.b["cutterIndex"] = self.camZmcIndex
                                    self.b["key"] = self.key
                                    self.b["firstPointX"] = self.cutLines[1][0]  # x坐标
                                    self.b["cutterIndex"] = self.camZmcIndex
                                    self.b["topToMiddle"] = self.cutLines[-4]
                                    self.b["topToBottom"] = self.cutLines[-3]
                                    self.b["topMiddleAngle"] = self.cutLines[-2]
                                    self.b["bottomMiddleAngle"] = self.cutLines[-1]
                                    self.b["cutDis"] = self.cutDis[0]
                                    self.b["cutArea"] = self.cutDis[1]
                                    self.b["cutWidth"] = self.cutWidth
                                    cutLines = self.change_cut_lines_order(self.cutLines)
                                    self.b["lines"] = cutLines
                                    self.linesQueue.put(self.b)
                                    self.logger.debug("id_{} 上料: 切线存放到切线队列中,存入切线: {},队列个数 = {}".format(self.camZmcIndex, cutLines,
                                                                                                                           self.linesQueue.qsize()))
                                    save_cut_line_image(self.camZmcIndex, self.imgCut, self.widthPix, self.heightPix, endwith="calc",
                                                        isFanzhuan=self.isFanzhuan)
                                    ret = self.camZmc.img_result_ok()
                                    if ret != 0:
                                        self.keep_zmc_read_write_error(ret, "camZmc", self.camZmcIndex)
                                        return ret
                                    self.camZmcIndex += 1
                                    if self.camZmcIndex > self.indexMax:
                                        self.camZmcIndex = 1
                                    newData = {
                                        "camZmcIndex": self.camZmcIndex,
                                        "cutId": self.cutId
                                    }
                                    self.update_working_info_to_db_and_web(newData)
                                    # 保存切线图
                            elif self.jointNumber == "3":
                                self.logger.debug("id_{} 尺寸验证通过,三拼板.".format(self.camZmcIndex))
                                self.b["isDummy"] = "否"
                                self.b["cutterIndex"] = self.camZmcIndex
                                self.b["key"] = self.key
                                self.b["firstPointX"] = self.cutLines[1][0]  # x坐标
                                self.b["cutter_index"] = self.camZmcIndex
                                self.b["firstBoardLen"] = self.cutLines[-4]  # 第一个板长
                                self.b["secondBoardLen"] = self.cutLines[-3]
                                self.b["thirdBoardLen"] = self.cutLines[-2]
                                angleList = self.cutLines[-1]
                                self.b["topFirstMiddleAngle"] = angleList[0]  # 第一边和第一个中缝的角度
                                self.b["bottomFirstMiddleAngle"] = angleList[1]  # 底边和第一个中缝的角度
                                self.b["twoMiddleAngle"] = angleList[2]  # 两个中缝之间的角度
                                self.b["bottomSecondMiddleAngle"] = angleList[3]  # 底边和第二个中缝的角度
                                self.b["cutDis"] = self.cutDis[0]  # 刀切割的长度
                                self.b["cutArea"] = self.cutDis[1]  # 铜箔切割掉的面积
                                cutLines = self.change_cut_lines_order(self.cutLines)
                                self.b["lines"] = cutLines
                                self.b["cutWidth"] = self.cutWidth
                                self.linesQueue.put(self.b)  # 首板的时候,切线不放到队列中,只有非首板的切线才放到队列中
                                self.logger.debug("id_{} 切线存放到切线队列中,放入后队列个数 = {}".format(
                                    self.camZmcIndex, self.linesQueue.qsize()))

                                save_three_cut_line_image(self.camZmcIndex, self.imgCut, self.widthPix, self.heightPix, endwith="calc",
                                                          isFanzhuan=self.isFanzhuan)

                                ret = self.camZmc.img_result_ok()
                                if ret != 0:
                                    self.keep_zmc_read_write_error(ret, 'camZmc', self.camZmcIndex)
                                    return ret
                                self.camZmcIndex += 1
                                if self.camZmcIndex > self.indexMax:
                                    self.camZmcIndex = 1
                                newData = {
                                    "camZmcIndex": self.camZmcIndex,
                                    "cutId": self.cutId
                                }
                                self.update_working_info_to_db_and_web(newData)
                            else:
                                boardHeight = self.singleBoardLen
                                boardWidth = self.cutWidth
                                heightError = boardHeight - self.singleBoardHeight
                                widthError = boardWidth - self.singleBoardWidth

                                info = 'id_{} 上料：单拼板,和首板尺寸验证通过,弹窗提示确认切线是否OK.偏差值,宽度:{:.2f},高度:{:.2f}'.format(
                                    self.camZmcIndex, widthError, heightError)
                                self.logger.debug(info)

                                self.b["boardIndex"] = self.camZmcIndex
                                self.b["key"] = self.key
                                self.b["firstPointX"] = self.cutLines[1][0]  # x坐标
                                self.b["cutterIndex"] = self.camZmcIndex
                                self.b["singleBoardWidth"] = self.cutWidth
                                self.b["singleBoardHeight"] = self.singleBoardLen
                                self.b["cutDis"] = self.cutDis[0]
                                self.b["cutArea"] = self.cutDis[1]
                                self.b["cutWidth"] = self.cutWidth
                                cutLines = self.change_cut_lines_order(self.cutLines)
                                self.b["lines"] = cutLines
                                self.linesQueue.put(self.b)  # 首板的时候,切线不放到队列中,只有非首板的切线才放到队列中
                                self.logger.debug("id_{} 上料:单拼板 切线存放到切线队列中,存入切线: {},队列个数 = {}".format(self.camZmcIndex, cutLines,
                                                                                                                             self.linesQueue.qsize()))
                                save_one_cut_line_image(self.camZmcIndex, self.imgCut, self.widthPix, self.heightPix, endwith="calc",
                                                        isFanzhuan=self.isFanzhuan)
                                ret = self.camZmc.img_result_ok()
                                if ret != 0:
                                    self.keep_zmc_read_write_error(ret, "camZmc", self.camZmcIndex)
                                    return ret
                                self.camZmcIndex += 1
                                if self.camZmcIndex > self.indexMax:
                                    self.camZmcIndex = 1
                                newData = {
                                    "camZmcIndex": self.camZmcIndex,
                                    "cutId": self.cutId
                                }
                                self.update_working_info_to_db_and_web(newData)

                    while self.webModify:
                        if self.deviceStates.workStopFlag:
                            self.logger.debug("id_{} workStopFlag = True,上料拍照任务退出!".format(self.camZmcIndex))
                            return
                        gevent.sleep(0.5)

            else:
                gevent.sleep(0.05)
                continue

    @staticmethod
    def get_web_show_image(imageOriginal, totalBoardCheckImage=False):
        """
        将要发送给前端的原图,按照缩放比例,进行缩放,然后显示到前端
        :param imageOriginal: 要发送给前端显示的原图
        :param totalBoardCheckImage:是否发送的是物料区域检测的时候的原图,这个时候图比较大,按照1000的尺寸去搞定
        :return:
        """
        if totalBoardCheckImage:
            print("ImageOriginal = ({},{})".format(imageOriginal.shape[1], imageOriginal.shape[0]))
            h, w = imageOriginal.shape[:2]
            newWidth = 1000
            newHeight = int((h / w) * newWidth)
            imageSend = cv.resize(imageOriginal, (newWidth, newHeight), cv.INTER_AREA)
            imageSend = common_tools.image_to_string(imageSend)
            return imageSend
        print("ImageOriginal = ({},{})".format(imageOriginal.shape[1], imageOriginal.shape[0]))
        h, w = imageOriginal.shape[:2]
        newWidth, newHeight = int(w / cp.sizeScale), int(h / cp.sizeScale)
        imageSend = cv.resize(imageOriginal, (newWidth, newHeight), cv.INTER_AREA)
        print("boardSized = ({};{})".format(imageSend.shape[1], imageSend.shape[0]))
        imageSend = common_tools.image_to_string(imageSend)
        return imageSend

    def change_cut_lines_order(self, cutLines):
        """
        更改切线的顺序
        :param cutLines:切线列表
        :return:
        """
        p1, p2, p3, p4, p5, p6, p7, p8, *_ = cutLines
        newLines = []
        if self.isFirstCut:
            newLines.append(p1)
            newLines.append(p2)
        if self.leftPix != 0:
            newLines.append(p7)
            newLines.append(p8)
        if self.isThirdCut:
            newLines.append(p3)
            newLines.append(p4)
        if self.rightPix != 0:
            newLines.append(p5)
            newLines.append(p6)
        return newLines

    def keep_order_calc_size(self):
        """
        保存工单做首板的时候确认的ok的尺寸信息
        :return:
        """
        self.widthMm = self.boardSizeList[self.sendIndex][0]
        self.heightMm = self.boardSizeList[self.sendIndex][1]
        self.widthPix = int(self.widthMm * cp.mm2pix)
        self.heightPix = int(self.heightMm * cp.mm2pix)
        # 保存为英寸
        try:
            newData = {
                "calcWidth": round(self.widthMm/ 25.4,2),
                "calcHeight": round(self.heightMm/25.4,2)
            }
            res, info = self.db.update_order(self.key, newData)
            if res != "ok":
                errorInfo = "in keep_order_calc_size() error: {}".format(info)
                print(errorInfo)
                self.logger.debug(errorInfo)

        except Exception as e:
            errorInfo = "in keep_order_calc_size() error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

    def web_request_board_back(self):
        """
        前端界面点击了板回退
        :return:
        """
        ret = self.camZmc.img_result_failed()
        self.waitBoardBack = True  # 板回退,将等待板回退的标志位置为True,在点击重新拍照的时候再置为False
        self.webModify = False  # 将等待界面选择的搞掉
        self.logger.debug("id_{} 上料: 界面上点击了板回退,板退到拍照起点!".format(self.camZmcIndex))
        return "ok" if ret == 0 else ret

    def web_request_re_get_image(self):
        """
        界面上点击了重新拍照
        :return:
        """
        # 如果已经点击了板回退按钮,这个时候直接重拍就可以了.
        self.logger.debug("id_{} 上料: 界面上点击了重新拍照,进行重拍!".format(self.camZmcIndex))
        ret = 0
        if self.waitBoardBack:
            self.waitBoardBack = False
        else:
            ret = self.camZmc.img_result_failed()

        self.webModify = False
        self.camZmcIndex += 1
        if self.camZmcIndex > self.indexMax:
            self.camZmcIndex = 1
        newData = {
            "camZmcIndex": self.camZmcIndex
        }
        self.update_working_info_to_db_and_web(newData)
        return "ok" if ret == 0 else ret

    def update_cut_info(self, key, boardNumbers, cutDis, cutArea, tableUsed):
        """
        更新裁切机切割的相关参数,就是作业了多少个板的问题.第一个是要更新工单里面的数据,第二个要更新统计数据列表里面的数据.
        @param key:
        @param boardNumbers:
        @param cutDis:
        @param cutArea:
        @param tableUsed:
        @return:
        """
        # 先更新本工单的一些数据,先获取到本工单的数据,然后再进行更新.
        try:
            data = self.db.get_order_by_key(key)
            amount = data.get("amount")
            newData = {
                "amount": amount + boardNumbers
            }
            self.db.update_order(key, newData)
            # 更新基本信息的一些数据,包括裁切铜箔面积,已做板数量,还有台布使用时间.
            data = self.db.get_working_info()[1]
            knifeUsed = round(float(data.get("knifeUsed")) + cutDis, 4)
            cutAreas = round(float(data.get("cutAreas")) + cutArea, 4)
            cutAmounts = int(data.get("cutAmounts")) + boardNumbers
            tableUsed = round(float(data.get("tableUsed")) + tableUsed, 5)

            newData = {
                "knifeUsed": knifeUsed,
                "cutAreas": cutAreas,
                "cutAmounts": cutAmounts,
                "tableUsed": tableUsed
            }
            self.db.save_working_info_data(newData)
            msgVal = {
                "val": self.key
            }
            send_order_state_to_ui(msgVal)
            self.logger.debug("id_{} 裁切机: 切割完成,基本信息和工单信息更新完成!".format(self.cbjZmcIndex))
            self.db.update_produce_record(boardNumbers)

        except Exception as e:
            errorInfo = "in update_cut_info() error : {}".format(str(e))
            self.logger.debug("id_{} {}".format(self.cbjZmcIndex, errorInfo))
            print(errorInfo)

    def update_working_info_to_db_and_web(self, newData):
        """
        更新workinfo的数据到数据库和前端
        :param newData: 新的数据值
        :return:
        """
        self.db.save_working_info_data(newData)
        send_working_info_state_to_ui()

    def web_request_total_board_is_ok(self):
        """
        界面上点击了物料确认ok
        :return:
        """
        self.logger.debug("id_{} 上料: 界面上点击了物料检测确认OK,人工确认物料OK,继续作业!".format(self.camZmcIndex))
        self.totalBoardIsOk = True
        self.webModify = False

    def web_request_next_board_res(self):
        """
        前端界面点击了下一张.
        :return:
        """
        self.sendIndex += 1
        if self.sendIndex >= len(self.boardResList):
            self.sendIndex = 0
        boardSend = self.get_web_show_image(self.boardResList[self.sendIndex])
        msgVal = {
            "val": "boardSizeCheck",
            "boardRes": boardSend,
            "boardSize": self.boardSizeList[self.sendIndex],
            "inputSize": (self.widthMm, self.heightMm),
            "boardCalcError": (round(self.boardSizeList[self.sendIndex][0] - self.widthMm, 2),
                               round(self.boardSizeList[self.sendIndex][1] - self.heightMm, 2))  # 误差
        }
        send_produce_state_to_ui(msgVal)
        return "ok"

    def web_request_last_board_res(self):
        """
        前端界面点击了上一张
        :return:
        """
        self.sendIndex -= 1
        if self.sendIndex < 0:
            self.sendIndex = len(self.boardResList) - 1
        boardSend = self.get_web_show_image(self.boardResList[self.sendIndex])
        msgVal = {
            "val": "boardSizeCheck",
            "boardRes": boardSend,
            "boardSize": self.boardSizeList[self.sendIndex],
            "inputSize": (self.widthMm, self.heightMm),
            "boardCalcError": (round(self.boardSizeList[self.sendIndex][0] - self.widthMm, 2),
                               round(self.boardSizeList[self.sendIndex][1] - self.heightMm, 2))  # 误差
        }
        send_produce_state_to_ui(msgVal)

    def web_request_keep_ok_size(self):
        """
        前端界面上点击了确认尺寸OK
        :return:
        """
        self.logger.debug("id_{} 上料:  物料号: {},界面上点击了尺寸确认OK,保存识别使用尺寸,并保存图像".format(
            self.camZmcIndex,self.cutId))
        imageKeep = self.boardResList[self.sendIndex]
        self.keep_order_calc_size()
        save_size_is_ok_image(self.cutId, imageKeep, self.widthPix, self.heightPix, endwith="calc")
        self.sizeCheckOkBtnClick = True

    def web_request_first_keep_ok_size(self):
        widthOffset = round(self.boardSizeList[self.sendIndex][0] - self.widthMm, 2)
        heightOffset = round(self.boardSizeList[self.sendIndex][1] - self.heightMm, 2)
        if cp.maxOrderSIzeErrorThres < widthOffset or widthOffset < cp.minOrderSIzeErrorThres or cp.maxOrderSIzeErrorThres < heightOffset or heightOffset < cp.minOrderSIzeErrorThres:
            info = "确认作业的 实际作业尺寸：{} 与 工单输入尺寸：{} 偏差较大，偏差阈值范围：{}，和工单输入尺寸相比 宽：{}， 高：{}；请认真确认是否要放弃本次操作，重新开始首板作业流程！".format(
                (self.boardSizeList[self.sendIndex]), (self.widthMm, self.heightMm), (cp.minOrderSIzeErrorThres, cp.maxOrderSIzeErrorThres),
                widthOffset, heightOffset)
            displayStatus = True
            self.logger.debug("id_{},{}".format(self.camZmcIndex, info))
            msgVal = {
                "val": "displayStatus",
                "displayStatus": displayStatus,
                "cutInfo": info
            }
            send_produce_state_to_ui(msgVal)
        else:
            self.web_request_keep_ok_size()

    def web_request_test_new_function(self):
        """
        测试新的功能,测试新的功能,主要用来测试一些新功能,看看能否成功.
        :return:
        """
        self.hand_annotated_modal_visble()
        return "ok"

    def web_request_hand_inner_board_size(self):
        """
        界面上点击了人工标注.这个时候标注框,让人工去标注内板尺寸.
        :return:
        """
        boardSend = self.get_web_show_image(self.upBoard)
        labelSize = [self.widthPix // cp.sizeScale, self.heightPix // cp.sizeScale]
        labelSizeMm = [self.widthMm, self.heightMm]
        msgVal = {
            "val": "handBoardSize",
            "upBoardRes": boardSend,
            "labelBoardSize": labelSize,
            "labelBoardSizeMm": labelSizeMm
        }
        send_produce_state_to_ui(msgVal)
        return "ok"

    def web_request_adjust_board_size(self, adjustSize):
        """
        前端界面修改了标注的尺寸的按钮
        :return:
        """
        self.boardSizeAdjustFlag = True  # 是否调整了尺寸
        widthAdjust, heightAdjust = adjustSize
        widthOrignal, heightOriginal = self.widthMm, self.heightMm
        widthOrignal += widthAdjust
        heightOriginal += heightAdjust

        self.widthAdjustAfter = widthOrignal
        self.heightAdjustAfter = heightOriginal

        boardSend = self.get_web_show_image(self.upBoard)
        labelSize = [self.widthAdjustAfter * cp.mm2pix // cp.sizeScale, self.heightAdjustAfter * cp.mm2pix // cp.sizeScale]
        labelSizeMm = [self.widthAdjustAfter, self.heightAdjustAfter]
        msgVal = {
            "val": "handBoardSize",
            "upBoardRes": boardSend,
            "labelBoardSize": labelSize,
            "labelBoardSizeMm": labelSizeMm
        }
        send_produce_state_to_ui(msgVal)
        return "ok"

    def web_request_re_hand_label_two_boxes(self):
        """
        前端界面点击了重新标注
        :return:
        """
        if self.jointNumber == "2":
            imageSend = self.get_web_show_image(self.totalBoard)
            # 计算缩放比例
            widthPix = (self.widthAdjustAfter * cp.mm2pix) if self.boardSizeAdjustFlag else self.widthPix
            heightPix = (self.heightAdjustAfter * cp.mm2pix) if self.boardSizeAdjustFlag else self.heightPix
            widthMm = (self.widthAdjustAfter if self.boardSizeAdjustFlag else self.widthMm)
            heightMm = (self.heightAdjustAfter if self.boardSizeAdjustFlag else self.heightMm)
            labelSizePix = [widthPix // cp.sizeScale, heightPix // cp.sizeScale]
            labelSizeMm = [widthMm, heightMm]
            info = "重新标注,请标注好内框位置,注意内框不含胶边,内框是内板和胶边的内边缘的分界点."
            msgVal = {
                "val": "handAnnotated",
                "totalBoard": imageSend,
                "labelSizeMm": labelSizeMm,
                "labelSizePix": labelSizePix,
                "cutInfo": info
            }
            send_produce_state_to_ui(msgVal)
            return "ok"
        elif self.jointNumber == "3":
            self.waitCheckBoardSize = False
            imageSend = self.get_web_show_image(self.totalBoard)
            # 计算缩放比例
            widthPix = (self.widthAdjustAfter * cp.mm2pix) if self.boardSizeAdjustFlag else self.widthPix
            heightPix = (self.heightAdjustAfter * cp.mm2pix) if self.boardSizeAdjustFlag else self.heightPix
            widthMm = (self.widthAdjustAfter if self.boardSizeAdjustFlag else self.widthMm)
            heightMm = (self.heightAdjustAfter if self.boardSizeAdjustFlag else self.heightMm)
            labelSizePix = [widthPix // cp.sizeScale, heightPix // cp.sizeScale]
            labelSizeMm = [widthMm, heightMm]
            info = "重新标注,请标注好内框位置,注意内框不含胶边,内框是内板和胶边的内边缘的分界点."
            msgVal = {
                "val": "visionCheckThree",
                "totalBoard": imageSend,
                "labelSizeMm": labelSizeMm,
                "labelSizePix": labelSizePix,
                "cutInfo": info
            }
            send_produce_state_to_ui(msgVal)
            return "ok"

    def web_request_hand_label_three_label(self, up_box_info, down_box_info, ground_box_info):
        """
                前端界面点击了标注,标注了三个内框位置
                :param upBoardInfo: 标注的上框的位置信息和角度
                :param downBoardInfo: 标注的中框的位置信息和角度
                :return:
                """
        upPoint = (np.array(up_box_info[0]) * cp.sizeScale).astype('int').tolist()  # 转换为全部是int的列表
        downPoint = (np.array(down_box_info[0]) * cp.sizeScale).astype('int').tolist()  # 转换为全部是int的列表
        ground_point = (np.array(ground_box_info[0]) * cp.sizeScale).astype('int').tolist()  # 转换为全部是int的列表
        if self.boardSizeAdjustFlag and self.isFirstBoard:
            self.widthMm = self.widthAdjustAfter
            self.heightMm = self.heightAdjustAfter
            self.widthPix = int(self.widthMm * cp.mm2pix)
            self.heightPix = int(self.heightMm * cp.mm2pix)
            newData = {
                "firstCheckWidth": self.widthMm,
                "firstCheckHeight": self.heightMm,
            }
            self.db.update_order(self.key, newData)

        upBox = self.get_box_rect_points(self.totalBoard, upPoint, up_box_info[1])
        downBox = self.get_box_rect_points(self.totalBoard, downPoint, down_box_info[1])
        groundBox = self.get_box_rect_points(self.totalBoard, ground_point, ground_box_info[1])
        assert upBox is not None
        assert downBox is not None
        assert groundBox is not None
        self.upBox = upBox
        self.middleBox = downBox
        self.groundBox = groundBox
        self.boxesRes = [self.upBox, self.middleBox, self.groundBox]
        cutLinesRes = get_lines_with_boxes_3p(self.lineImage, self.totalBoard, self.totalBoardBox,
                                              self.totalBoardThresWithAngle, self.boxesRes, self.transMtx,
                                              self.topPix, self.leftPix, self.bottomPix,
                                              self.rightPix, self.totalBoardOffset)
        self.cutLines, self.imgCut, self.cutDis, self.isFirstCut, self.isThirdCut, self.cutWidth = cutLinesRes
        imageSend = self.get_web_show_image(self.imgCut)
        info = "获取切线OK,请确认切线位置是否正确."
        msgVal = {
            "val": "cutLineCheck",
            "cutImage": imageSend,
            "cutInfo": info
        }
        send_produce_state_to_ui(msgVal)
        self.logger.debug("id_ {} 上料: 界面上点击了标注,识别的切线结果: {}".format(self.camZmcIndex, self.cutLines))
        return "ok"

    def web_request_hand_label_two_boxes(self, upBoardInfo, downBoardInfo):
        """
        前端界面点击了标注,标注了两个内框位置
        :param upBoardInfo: 标注的上框的位置信息和角度
        :param downBoardInfo: 标注的下框的位置信息和角度
        :return:
        """
        upPoint = (np.array(upBoardInfo[0]) * cp.sizeScale).astype('int').tolist()  # 转换为全部是int的列表
        downPoint = (np.array(downBoardInfo[0]) * cp.sizeScale).astype('int').tolist()  # 转换为全部是int的列表
        if self.boardSizeAdjustFlag and self.isFirstBoard:
            self.widthMm = self.widthAdjustAfter
            self.heightMm = self.heightAdjustAfter
            self.widthPix = int(self.widthMm * cp.mm2pix)
            self.heightPix = int(self.heightMm * cp.mm2pix)
            newData = {
                "firstCheckWidth": self.widthMm,
                "firstCheckHeight": self.heightMm,
            }
            self.db.update_order(self.key, newData)

        upBox = self.get_box_rect_points(self.totalBoard, upPoint, upBoardInfo[1])
        downBox = self.get_box_rect_points(self.totalBoard, downPoint, downBoardInfo[1])
        assert upBox is not None
        assert downBox is not None
        self.upBox = upBox
        self.downBox = downBox
        cutLinesRes = get_lines_with_boxes(self.lineImage, self.totalBoard, self.totalBoardBox, self.totalBoardThresWithAngle,
                                           self.upBox, self.downBox, self.transMtx, self.topPix, self.leftPix,
                                           self.bottomPix, self.rightPix, self.totalBoardOffset)

        self.cutLines, self.imgCut, self.cutDis, self.isFirstCut, self.isThirdCut, self.cutWidth = cutLinesRes
        imageSend = self.get_web_show_image(self.imgCut)
        info = "获取切线OK,请确认切线位置是否正确."
        msgVal = {
            "val": "cutLineCheck",
            "cutImage": imageSend,
            "cutInfo": info
        }
        send_produce_state_to_ui(msgVal)
        self.logger.debug("id_ {} 上料: 界面上点击了标注,识别的切线结果: {}".format(self.camZmcIndex, self.cutLines))
        return "ok"

    def web_request_adjust_board_size_two_box(self, adjustSize):
        """
        # 标注两个框的时候调整尺寸
        :param adjustSize: 调整的尺寸
        :return:
        """
        self.boardSizeAdjustFlag = True
        widthAdjust, heightAdjust = adjustSize
        widthOrignal, heightOriginal = self.widthMm, self.heightMm
        widthOrignal += widthAdjust
        heightOriginal += heightAdjust

        self.widthAdjustAfter = widthOrignal
        self.heightAdjustAfter = heightOriginal

        imageSend = self.get_web_show_image(self.totalBoard)

        msgVal = {
            "val": "handAnnotated",
            "totalBoard": imageSend,
            "labelSize": [self.widthAdjustAfter, self.heightAdjustAfter],
            "cutInfo": "请确认标注准确"
        }
        send_produce_state_to_ui(msgVal)
        return "ok"

    def get_box_rect_points(self, board, point, angle):
        """
        根据图像,以及左上角的点和角度获取标注的矩形
        :param board: 传递过来的整个物料框,矩形的左上角
        :param point: 最左边的点,angle,旋转角度
        :param angle:
        :return:
        """
        widthPix = int(self.widthAdjustAfter * cp.mm2pix) if self.boardSizeAdjustFlag else self.widthPix
        heightPix = int(self.heightAdjustAfter * cp.mm2pix) if self.boardSizeAdjustFlag else self.heightPix
        h, w = board.shape[:2]
        pointList = [[point[0] + widthPix, point[1]],
                     [point[0] + widthPix, point[1] + heightPix],
                     [point[0], point[1] + heightPix]]  # 矩形框的另外三个点
        newPoints = []  # 存放新的坐标点
        newPoints.append(point)
        for p in pointList:
            x1 = p[0]
            y1 = h - p[1]
            x2 = point[0]
            y2 = h - point[1]
            x = int((x1 - x2) * np.cos(angle * np.pi / 180.0) - (y1 - y2) * np.sin(angle * np.pi / 180.0) + x2)
            y = int((x1 - x2) * np.sin(angle * np.pi / 180.0) + (y1 - y2) * np.cos(angle * np.pi / 180.0) + y2)
            y = h - y
            newPoints.append([x, y])
        return newPoints

    def web_request_hand_inner_board_size_is_ok(self, boardInfo):
        """
        界面上点击了标注内板尺寸,这个时候把这个标注的图保存到数据库.
        :param boardInfo:boardInfo,左上角的点,以及角度.
        :return:
        """
        self.logger.debug("id_{} 上料: 界面上点击了标注内板尺寸OK按钮.更新尺寸信息".format(self.camZmcIndex))
        self.waitHandLabelInnerBoard = False
        self.waitCheckBoardSize = False
        if self.boardSizeAdjustFlag:
            self.widthMm = self.widthAdjustAfter
            self.heightMm = self.heightAdjustAfter
            self.boardSizeAdjustFlag = False
        self.widthPix = int(self.widthMm * cp.mm2pix)
        self.heightPix = int(self.heightMm * cp.mm2pix)
        try:
            newData = {
                "calcWidth": self.widthMm,
                "calcHeight": self.heightMm
            }
            res, info = self.db.update_order(self.key, newData)
            if res != "ok":
                errorInfo = "in web_request_hand_inner_board_size_is_ok() error: {}".format(info)
                print(errorInfo)
                self.logger.debug(errorInfo)
        except Exception as e:
            errorInfo = "in web_request_hand_inner_board_size_is_ok() error: {}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

        leftTop, angle = boardInfo
        leftTop = [int(leftTop[0] * cp.sizeScale), int(leftTop[1] * cp.sizeScale)]
        upBoxPoints = self.get_box_rect_points(self.upBoard, leftTop, angle)
        keepImage = self.upBoard.copy()
        cv.drawContours(keepImage, [np.array(upBoxPoints)], -1, (0, 255, 255), 3)
        save_size_is_ok_image(self.cutId, keepImage, self.widthPix, self.heightPix,
                              "handLabel", self.isFanzhuan)
        return "ok"

    def web_reqeust_cut_board(self, edgeRemainList):
        """
        界面上点击了切板
        :param edgeRemainList:界面上使用的留边宽度
        :return:
        """
        self.topMm, self.leftMm, self.bottomMm, self.rightMm = edgeRemainList
        self.topPix = int(self.topMm * cp.mm2pix)
        self.leftPix = int(self.leftMm * cp.mm2pix)
        self.bottomPix = int(self.bottomMm * cp.mm2pix)
        self.rightPix = int(self.rightMm * cp.mm2pix)
        if self.isFirstBoard:
            if self.jointNumber == "2":
                self.firstCheckBoardLen1 = self.cutLines[-4]
                self.firstCheckBoardLen2 = self.cutLines[-3] - self.cutLines[-4]
                self.logger.debug("id_{} 上料: 首板,点击切板,首板尺寸值,第一块 = {:.2f},第二块 = {:.2f}".format(
                    self.camZmcIndex, self.firstCheckBoardLen1, self.firstCheckBoardLen2))
                # 然后将偏小阈值该为8
                self.calcThresSmall = 8
                self.calcThresBig = 20
            elif self.jointNumber == "3":
                self.first_check_board1 = self.cutLines[-4]
                self.first_check_board2 = self.cutLines[-3]
                self.first_check_board3 = self.cutLines[-2]
                self.logger.debug("id_{} 首板,三拼板,点击切板,首板尺寸,第一块={},第二块={},第三块={}".format(
                    self.camZmcIndex, self.first_check_board1, self.first_check_board2, self.first_check_board3))
                firstCheckWidth, firstCheckHeight = self.get_size_by_first_check(self.upBox, self.downBox, self.middleBox)
                self.logger.debug("id_{} 首板,三拼板,内板尺寸记录,作业使用尺寸 = ({:.2f},{:.2f}),识别 = ({:.2f},{:.2f}),误差 = ({:.2f},{:.2f})".format(
                    self.camZmcIndex, self.widthMm, self.heightMm, firstCheckWidth, firstCheckHeight,
                    firstCheckWidth - self.widthMm, firstCheckHeight - self.heightMm))
                self.calcThresSmall = 9
                self.calcThresBig = 20
            else:
                self.singleBoardWidth = self.cutWidth
                self.singleBoardHeight = self.singleBoardLen
                self.logger.debug("id_{} 上料: 单拼板,首板,点击切板,首板尺寸值,切割宽度 = {:.2f},切割高度 = {:.2f}".format(
                    self.camZmcIndex, self.singleBoardWidth, self.singleBoardHeight))
                self.calcThresSmall = 8
                self.calcThresBig = 20
        else:
            if self.jointNumber == "2":
                firstCheckBoardLen1 = self.cutLines[-4]
                firstCheckBoardLen2 = self.cutLines[-3] - self.cutLines[-4]
                self.logger.debug("id_{} 上料: 非首板,点击切板,当前尺寸,第一块 = {:.2f},第二块 = {:.2f}".format(
                    self.camZmcIndex, firstCheckBoardLen1, firstCheckBoardLen2))
            elif self.jointNumber == "3":
                self.logger.debug("id_{} 非首板,三拼板,点击切板,当前的尺寸,第一块={:.2f},第二块={:.2f},第三块={:.2f}".format(
                    self.camZmcIndex, self.cutLines[-4], self.cutLines[-3], self.cutLines[-2]))
            else:
                self.logger.debug("id_{} 上料: 单拼板,非首板,点击切板,首板尺寸值,切割宽度 = {:.2f},切割高度 = {:.2f}".format(
                    self.camZmcIndex, self.singleBoardWidth, self.singleBoardHeight))

        # 如果是首板点击了切板,要把四个切线的位置保存进去,并且重新保存识别成功的图.
        if self.isFirstBoard:
            self.upLineOffset = [0, 0, 0, 0]
            self.downLineOffset = [0, 0, 0, 0]
            if self.jointNumber == '2':
                save_calc_ok_image(self.camZmcIndex, self.lineImage, self.widthPix, self.heightPix, self.upLineOffset, self.downLineOffset,
                                   self.isFanzhuan)
            elif self.jointNumber == "3":
                save_three_calc_ok_image(self.camZmcIndex, self.lineImage, self.widthPix, self.heightPix, self.upLineOffset, self.downLineOffset,
                                         self.isFanzhuan)
            else:
                save_one_calc_ok_image(self.camZmcIndex, self.lineImage, self.widthPix, self.heightPix, self.upLineOffset, self.downLineOffset,
                                       self.isFanzhuan)

            if self.jointNumber == 2:
                self.upLineOffset, self.downLineOffset = get_line_offset(self.upBox, self.downBox, self.totalBoard,
                                                                         self.totalBoardBox)
                # 判断是否是长铜箔,并且把长铜箔加一个停板延时,长铜箔的判定目前暂定为100个mm,如果大于90个mm,如果大于90个mm就认为是长铜箔.
                if self.upLineOffset[0] > cp.longCropperLimit * cp.mm2pix or self.downLineOffset[
                    2] > cp.longCropperLimit * cp.mm2pix:
                    isLongCropper = True
                else:
                    isLongCropper = False
                ret = self.camZmc.set_release_board_delay(isLongCropper)
                if ret != 0:
                    self.keep_zmc_read_write_error(ret, "camZmc", self.camZmcIndex)
                    return
                else:
                    self.logger.debug("id_{} 上料: 首板,是否长铜箔: {} 设置成功,铜箔长度:({:.2f},{:.2f}),长铜箔阈值: {}".format(
                        self.camZmcIndex, isLongCropper, self.upLineOffset[0] / cp.mm2pix, self.downLineOffset[2] / cp.mm2pix,
                        cp.longCropperLimit))
            save_first_board_cut_image(self.camZmcIndex, self.imgCut, self.widthPix, self.heightPix, endwith="first_cut", isFanzhuan=self.isFanzhuan)
        else:
            if self.jointNumber == '2':
                save_cut_line_image(self.camZmcIndex, self.imgCut, self.widthPix, self.heightPix, endwith="calc", isFanzhuan=self.isFanzhuan)
            elif self.jointNumber == "3":
                save_three_cut_line_image(self.camZmcIndex, self.imgCut, self.widthPix, self.heightPix, endwith="calc", isFanzhuan=self.isFanzhuan)
            else:
                save_one_cut_line_image(self.camZmcIndex, self.imgCut, self.widthPix, self.heightPix, endwith="calc", isFanzhuan=self.isFanzhuan)

        ret = self.camZmc.img_result_ok()
        if ret != 0:
            self.keep_zmc_read_write_error(ret, "camZmc", self.camZmcIndex)
            return ret

        # 切线顺序调换,将切线放入到队列中.
        self.b["cutterIndex"] = self.camZmcIndex
        self.b["key"] = self.key
        self.b["firstPointX"] = self.cutLines[1][0]  # x坐标
        if self.jointNumber == "2":
            self.b["cutterIndex"] = self.camZmcIndex
            self.b["topToMiddle"] = self.cutLines[-4]
            self.b["topToBottom"] = self.cutLines[-3]
            self.b["topMiddleAngle"] = self.cutLines[-2]
            self.b["bottomMiddleAngle"] = self.cutLines[-1]
            self.b["cutDis"] = self.cutDis[0]
            self.b["cutArea"] = self.cutDis[1]
            self.b["cutWidth"] = self.cutWidth
        elif self.jointNumber == "3":
            self.b["cutterIndex"] = self.camZmcIndex
            self.b["firstBoardLen"] = self.cutLines[-4]  # 第一个板长
            self.b["secondBoardLen"] = self.cutLines[-3]
            self.b["thirdBoardLen"] = self.cutLines[-2]
            angleList = self.cutLines[-1]
            self.b["topFirstMiddleAngle"] = angleList[0]  # 第一边和第一个中缝的角度
            self.b["bottomFirstMiddleAngle"] = angleList[1]  # 底边和第一个中缝的角度
            self.b["twoMiddleAngle"] = angleList[2]  # 两个中缝之间的角度
            self.b["bottomSecondMiddleAngle"] = angleList[3]  # 底边和第二个中缝的角度
            self.b["cutDis"] = self.cutDis[0]  # 刀切割的长度
            self.b["cutArea"] = self.cutDis[1]  # 铜箔切割掉的面积
            self.b["cutWidth"] = self.cutWidth
        else:
            self.b["singleBoardWidth"] = self.cutWidth
            self.b["singleBoardHeight"] = self.singleBoardLen
            self.b["cutDis"] = self.cutDis[0]
            self.b["cutArea"] = self.cutDis[1]
            self.b["cutWidth"] = self.cutWidth
        cutLines = self.change_cut_lines_order(self.cutLines)
        self.b["lines"] = cutLines
        self.linesQueue.put(self.b)
        self.logger.debug("id_{} 上料: 切线存放到切线队列中,存入切线: {},放入后切线队列个数 = {}".format(self.camZmcIndex, cutLines,
                                                                                                         self.linesQueue.qsize()))
        # 留边宽度更新到工单里面去
        newData = {
            "topEdge": self.topMm,
            "leftEdge": self.leftMm,
            "bottomEdge": self.bottomMm,
            "rightEdge": self.rightMm,
        }
        self.db.update_order(self.key, newData)
        self.db.web_request_get_order_by_key(self.key)
        self.webModify = False
        self.isFirstBoard = False
        self.camZmcIndex += 1
        if self.camZmcIndex > self.indexMax:
            self.camZmcIndex = 1
        newData = {
            "camZmcIndex": self.camZmcIndex,
            "cutId": self.cutId
        }
        self.update_working_info_to_db_and_web(newData)

    def web_reqeust_get_cut_lines_after_edge_change(self, edgeRemainList):
        """
        前端界面上调整了留边宽度
        :param edgeRemainList:留边宽度的列表,self.topEdge,self.leftEdge,self.bottomEdge,self.rightEdge
        :return:
        """
        edgeRemainListPix = [x * cp.mm2pix for x in edgeRemainList]
        topPix, leftPix, bottomPix, rightPix = edgeRemainListPix
        topMm, leftMm, bottomMm, rightMm = edgeRemainList
        if self.jointNumber == "2":
            cutLinesRes = get_lines_with_boxes(self.lineImage, self.totalBoard, self.totalBoardBox, self.totalBoardThresWithAngle,
                                               self.upBox, self.downBox, self.transMtx, topPix, leftPix,
                                               bottomPix, rightPix, self.totalBoardOffset)

            self.cutLines, self.imgCut, self.cutDis, self.isFirstCut, self.isThirdCut, self.cutWidth = cutLinesRes

            info = "识别成功"
            imageSend = self.get_web_show_image(self.imgCut)
            for point in self.cutLines[:-4]:
                if point[0] > self.cbjLimitX or point[0] < 0 or point[1] > self.cbjLimitY or point[1] < 0:
                    info = "切线超限,请确认裁切机X和Y的极限值是否正确!xLimit={},yLimit={}".format(self.cbjLimitX, self.cbjLimitY)
                    break
            msgVal = {
                "val": "cutLineCheck",
                "cutImage": imageSend,
                "cutInfo": info
            }
            send_produce_state_to_ui(msgVal)
            self.logger.debug("id_{} 上料: 手动调整留边宽度:top={},left={},bottom={},right={},结果: {}".format(self.camZmcIndex, topMm, leftMm,
                                                                                                               bottomMm, rightMm, info))
            return "ok"
        elif self.jointNumber == "3":
            cutLinesRes = get_lines_with_boxes_3p(self.lineImage, self.totalBoard, self.totalBoardBox,
                                                  self.totalBoardThresWithAngle, self.boxesRes, self.transMtx,
                                                  topPix, leftPix, bottomPix, rightPix, self.totalBoardOffset)
            self.cutLines, self.imgCut, self.cutDis, self.isFirstCut, self.isThirdCut, self.cutWidth = cutLinesRes
            info = "识别成功"
            imageSend = self.get_web_show_image(self.imgCut)
            for point in self.cutLines[:-4]:
                if point[0] > self.cbjLimitX or point[0] < 0 or point[1] > self.cbjLimitY or point[1] < 0:
                    info = "切线超限,请确认裁切机X和Y的极限值是否正确!xLimit={},yLimit={}".format(self.cbjLimitX, self.cbjLimitY)
                    break
            msgVal = {
                "val": "cutLineCheck",
                "cutImage": imageSend,
                "cutInfo": info
            }
            send_produce_state_to_ui(msgVal)
            self.logger.debug("id_{} 上料: 手动调整留边宽度:top={},left={},bottom={},right={},结果: {}".format(self.camZmcIndex, topMm, leftMm,
                                                                                                               bottomMm, rightMm, info))
            return "ok"
        else:
            cutLinesRes = get_lines_with_boxes_1p(self.lineImage, self.totalBoard, self.totalBoardBox, self.totalBoardThresWithAngle,
                                                                      self.singleBox, self.transMtx, topPix, leftPix, bottomPix, rightPix, self.totalBoardOffset)
            self.cutLines, self.imgCut, self.isFirstCut, self.isThirdCut, self.singleBoardLen, self.cutWidth, self.cutDis = cutLinesRes
            info = "识别成功"
            imageSend = self.get_web_show_image(self.imgCut)
            for point in self.cutLines[:-4]:
                if point[0] > self.cbjLimitX or point[0] < 0 or point[1] > self.cbjLimitY or point[1] < 0:
                    info = "切线超限,请确认裁切机X和Y的极限值是否正确!xLimit={},yLimit={}".format(self.cbjLimitX, self.cbjLimitY)
                    break
            msgVal = {
                "val": "cutLineCheck",
                "cutImage": imageSend,
                "cutInfo": info
            }
            send_produce_state_to_ui(msgVal)
            self.logger.debug("id_{} 上料: 手动调整留边宽度:top={},left={},bottom={},right={},结果: {}".format(self.camZmcIndex, topMm, leftMm,
                                                                                                               bottomMm, rightMm, info))
            return "ok"


    def open_cutter_final_check_dir(self):
        """
        打开终检原图目录
        :return:
        """
        return open_dir_by_file_path(CUTTER_RAW_FINAL_DIR)

    def open_cutter_final_res_dir(self):
        """
        打开终检结果目录
        :return:
        """
        return open_dir_by_file_path(CUTTER_RAW_FINAL_RES_DIR)

    def web_request_save_order(self, orderInfo):
        """
        :param orderInfo:
        :return:
        """
        saveKey = orderInfo["key"]
        if saveKey == self.key:
            # 如果要保存的工单和正在做的工单的key是相同的,就将工单的信息重新加载一遍
            self.order_info_init(orderInfo, isFirstBoard=False)  # 首板的标志去除掉
            return self.db.web_request_save_order(orderInfo)
        else:
            return self.db.web_request_save_order(orderInfo)

    def hand_annotated_modal_visble(self):
        # 先加载参数
        filePath = r"D:\xizhen_01"
        res, data = db.get_line_cam_check_thres()
        calcThresBig = 20
        calcThresSmall = 9
        innerToEdgeThres = 5

        keepDir = filePath
        if not os.path.isdir(keepDir):
            keepDir, _ = os.path.split(filePath)

        keepOkPath = os.path.join(keepDir, "ok")
        keepOkResPath = os.path.join(keepDir, "cutlines")
        keepFailPath = os.path.join(keepDir, "fail")
        if not os.path.exists(keepOkPath):
            os.mkdir(keepOkPath)
        if not os.path.exists(keepOkResPath):
            os.mkdir(keepOkResPath)
        if not os.path.exists(keepFailPath):
            os.mkdir(keepFailPath)

        fileNames = []  # 保存要去识别的所有的图片
        if os.path.isfile(filePath):
            filePath, fileName = os.path.split(filePath)
            fileNames.append(fileName)
        else:
            for root, dirs, files in os.walk(filePath):
                if root != filePath:
                    continue
                for file in files:
                    if file[-4:] != ".bmp":
                        continue
                    fileNames.append(file)
        self.transMtx = [
            [0, 1, 0],
            [1, 0, 0]
        ]
        self.topPix, self.leftPix, self.bottomPix, self.rightPix = [15, 15, 15, 15]
        for index, fileName in enumerate(fileNames):
            filePathReal = os.path.join(filePath, fileName)
            srcW, srcH, upLineOffset, downLineOffset = get_width_height_by_image_name_2p(fileName)
            if srcW is None or srcH is None:
                print("*" * 10 + "第 {} 张图 ({}),图像名称错误,已经跳过".format(index + 1, fileName) + "*" * 10)
                continue
            if 0 in upLineOffset or 0 in downLineOffset:
                isFirstBoard = True
            else:
                isFirstBoard = False
            print("*" * 10 + " 第 {} 张图 ({})".format(index + 1, fileName) + "*" * 10)
            imageTest = cv.imread(filePathReal, cv.IMREAD_GRAYSCALE)
            calcStart = time.time()
            self.lineImage = imageTest
            totalBoardRes = check_total_board_is_ok(self.lineImage, self.totalHeightMax, self.totalHeightMin, self.boardBreak,
                                                    self.jointNumber)
            checkRes, hintInfo, totalBoardBox, totalBoardNoAngle, totalBoardOffset, \
                totalBoardThresWithAngle, totalBoardResWithAngle = totalBoardRes
            self.totalBoardBox = totalBoardBox
            if checkRes:
                print("物料检测通过: {}".format(hintInfo))
            else:
                print("物料检测未通过: {}".format(hintInfo))

            if isFirstBoard:
                #
                res, info, upBox, downBox, totalBoard, totalBoardThres = get_boxes_first(imageTest, srcW, srcH, calcThresBig,
                                                                                         calcThresSmall,
                                                                                         totalBoardBox, totalBoardResWithAngle, self.isFanzhuan)
                self.totalBoardThresWithAngle = totalBoardThres
                upLineOffset, downLineOffset = get_line_offset(upBox, downBox, totalBoard, totalBoardBox)
                print("upLineOffset = {},downLineOffset = {}".format(upLineOffset, downLineOffset))
                self.totalBoard = totalBoard
                imageSend = self.get_web_show_image(self.totalBoard)
                labelSize = [self.widthPix // cp.sizeScale, self.heightPix // cp.sizeScale]
                msgVal = {
                    "val": "handBoardSizeState",
                    "upBoardRes": imageSend,
                    "labelBoardSize": labelSize,
                }
                send_produce_state_to_ui(msgVal)
            else:
                self.widthPix = 1900
                self.heightPix = 1714
                res, info, upBox, downBox, totalBoard, totalBoardThres = get_boxes(imageTest, srcW, srcH, calcThresBig,
                                                                                   calcThresSmall, innerToEdgeThres,
                                                                                   totalBoardBox, totalBoardThresWithAngle,
                                                                                   upLineOffset, downLineOffset, self.isFanzhuan)
                self.totalBoard = totalBoard
                self.totalBoardThresWithAngle = totalBoardThres
                imageSend = self.get_web_show_image(self.totalBoard)
                # 计算缩放比例
                labelSizePix = [self.widthPix // cp.sizeScale, self.heightPix // cp.sizeScale]
                self.widthMm = round(self.widthPix / cp.mm2pix, 2)
                self.heightMm = round(self.heightPix / cp.mm2pix, 2)
                labelSizeMm = [self.widthMm, self.heightMm]
                msgVal = {
                    "val": "handAnnotated",
                    "totalBoard": imageSend,
                    "labelSizeMm": labelSizeMm,
                    "labelSizePix": labelSizePix,
                    "cutInfo": info
                }
            self.webModify = True
            send_produce_state_to_ui(msgVal)
            save_calc_error_image(self.camZmcIndex, self.lineImage, self.widthPix, self.heightPix, self.upLineOffset, self.downLineOffset,
                                  self.isFanzhuan)

    def re_get_image(self):
        """
        计算出图片上的感应器位置与物料位置距离差距过大，重新拍照
        :return:
        """
        # 如果已经点击了板回退按钮,这个时候直接重拍就可以了.

        ret = self.camZmc.img_result_failed()
        self.webModify = False
        self.camZmcIndex += 1
        if self.camZmcIndex > self.indexMax:
            self.camZmcIndex = 1
        newData = {
            "camZmcIndex": self.camZmcIndex
        }
        self.update_working_info_to_db_and_web(newData)
        return "ok" if ret == 0 else ret

    # 获取识别的框的宽高
    def get_size_by_first_check(self, upBox, downBox, middleBox=None):
        upLeftTop, upRightTop, upRightBottom, upLeftBottom = upBox
        downLeftTop, downRightTop, downRightBottom, downLeftBottom = downBox
        middleLeftTop, middleRightTop, middleRightBottom, middleLeftBottom = downBox
        if middleBox is None:
            firstCheckWidth = round((distance(upLeftTop, upRightTop) + distance(downLeftBottom, downRightBottom)) / 2 / cp.mm2pix, 2)
            firstCheckHeight = round((distance(upLeftTop, upLeftBottom) + distance(downLeftBottom, downLeftTop)) / 2 / cp.mm2pix, 2)
        else:
            firstCheckWidth = round((distance(upLeftTop, upRightTop) + distance(downLeftBottom, downRightBottom) + distance(
                middleLeftTop, middleRightTop)) / 3 / cp.mm2pix, 2)
            firstCheckHeight = round((distance(upLeftTop, upLeftBottom) + distance(downLeftBottom, downLeftTop) + distance(
                middleLeftBottom, middleLeftTop)) / 3 / cp.mm2pix, 2)
        return firstCheckWidth, firstCheckHeight

    def web_request_offline_Online_Start_Board(self):
        if self.deviceStates.offlineOnlineStart:
            ret = self.offlineZmc.set_modbus_bit(15, 0)
            if ret != 0:
                errorMsg = "in web_request_offline_Online_Start_Board() 设置 modbus_bit({}) = {} 失败. ".format(15, 0)
                return errorMsg
        else:
            ret = self.offlineZmc.set_modbus_bit(15, 1)
            if ret != 0:
                errorMsg = "in web_request_offline_Online_Start_Board() 设置 modbus_bit({}) = {} 失败. ".format(15, 1)
                return errorMsg
        return 'ok'

    def web_request_guard_bar_access_shield(self):
        if self.deviceStates.shieldStart[0]:
            ret = self.offlineZmc.set_modbus_bit(200, 0)
            if ret != 0:
                errorMsg = "in web_request_guard_bar_access_shield() 设置 modbus_bit({}) = {} 失败. ".format(15, 0)
                return errorMsg
        else:
            ret = self.offlineZmc.set_modbus_bit(200, 1)
            if ret != 0:
                errorMsg = "in web_request_guard_bar_access_shield() 设置 modbus_bit({}) = {} 失败. ".format(15, 1)
                return errorMsg
        return 'ok'

    def web_request_buffer_table_counter_shielding(self):
        if self.deviceStates.shieldStart[1]:
            ret = self.offlineZmc.set_modbus_bit(201, 0)
            if ret != 0:
                errorMsg = "in web_request_buffer_table_counter_shielding() 设置 modbus_bit({}) = {} 失败. ".format(15, 0)
                return errorMsg
        else:
            ret = self.offlineZmc.set_modbus_bit(201, 1)
            if ret != 0:
                errorMsg = "in web_request_buffer_table_counter_shielding() 设置 modbus_bit({}) = {} 失败. ".format(15, 1)
                return errorMsg
        return 'ok'

    def web_request_cache_cart_shielding(self):
        if self.deviceStates.shieldStart[3]:
            ret = self.offlineZmc.set_modbus_bit(203, 0)
            if ret != 0:
                errorMsg = "in web_request_cache_cart_shielding() 设置 modbus_bit({}) = {} 失败. ".format(15, 0)
                return errorMsg
        else:
            ret = self.offlineZmc.set_modbus_bit(203, 1)
            if ret != 0:
                errorMsg = "in web_request_cache_cart_shielding() 设置 modbus_bit({}) = {} 失败. ".format(15, 1)
                return errorMsg
        return 'ok'

    def web_request_offline_feeding_table_shielding(self):
        if self.deviceStates.shieldStart[4]:
            ret = self.offlineZmc.set_modbus_bit(204, 0)
            if ret != 0:
                errorMsg = "in web_request_offline_feeding_table_shielding() 设置 modbus_bit({}) = {} 失败. ".format(15, 0)
                return errorMsg
        else:
            ret = self.offlineZmc.set_modbus_bit(204, 1)
            if ret != 0:
                errorMsg = "in web_request_offline_feeding_table_shielding() 设置 modbus_bit({}) = {} 失败. ".format(15, 1)
                return errorMsg
        return 'ok'

    def web_request_start_produce(self):
        """
        展示的时候请求启动设备
        :return:
        """
        ret = self.cutterZmc.cutter_zmc_start()  # 设备启动
        if ret != 0:
            return "铡刀控制器通信异常,设备启动失败"
        self.cutterZmc.set_two_board_joint_flag()
        msgVlue = {
            "val": "updateStartState",
            "startStateVisble": True,
            "info": "设备启动中...",
        }
        send_line_camera_cal_state_to_ui(msgVlue)
        self.startStateCheck = True  # 设备状态检测状态为True
        deviceStartTime = time.time()
        msgVal = {
            "val": "produceStartModal",
            "modalVisible": True,
            "produceStartInfo": "设备启动中..."
        }
        send_produce_state_to_ui(msgVal)
        gevent.sleep(0.5)
        self.produceStartCheck = True
        while self.produceStartCheck:
            _, val1 = self.camZmc.is_running()
            _, val2 = self.cbjZmc.is_running()
            _, val3 = self.cutterZmc.is_running()
            info1 = "1. 上料启动中..." if val1 == 0 else "1. 上料启动成功!"
            info2 = "2. 裁切机启动中..." if val2 == 0 else "2. 裁切机启动成功!"
            info3 = "3. 铡刀启动中..." if val3 == 0 else "3. 铡刀启动成功!"
            if val1 == 1 and val2 == 1 and val3 == 1:
                self.produceStartCheck = False
                msgVal = {
                    "val": "produceStartModal",
                    "modalVisible": False,
                    "produceStartInfo": ""
                }
            else:
                msgVal = {
                    "val": "produceStartModal",
                    "modalVisible": True,
                    "produceStartInfo": info1 + " " + info2 + " " + info3
                }
                send_produce_state_to_ui(msgVal)
        gevent.sleep(0.1)
        msgVal = {
            "val": "produceStartModal",
            "modalVisible": False,
            "produceStartInfo": ""
        }
        send_produce_state_to_ui(msgVal)
        gevent.sleep(0.3)
        # 自动作业启动成功以后,开启后台的线程.
        self.deviceStates.workStopFlag = False
        self.zhanshiThead = threading.Thread(target=self.zhanshi, name="zhanshiThead")
        self.zhanshiThead.setDaemon(True)
        self.zhanshiThead.start()
        return "ok"

    def zhanshi(self):
        self.logger.debug("流程展示作业开启!")
        self.camZmcThead = threading.Thread(target=self.cam_zmc, name="camZmcThead")
        self.cbjZmcThead = threading.Thread(target=self.cbj_zmc, name="cbjZmcThead")
        self.cutterZmcThread = threading.Thread(target=self.cutter_zmc, name="cutterZmcThread")
        self.camZmcThead.setDaemon(True)
        self.cbjZmcThead.setDaemon(True)
        self.cutterZmcThread.setDaemon(True)
        self.camZmcThead.start()
        self.cbjZmcThead.start()
        self.cutterZmcThread.start()

        while not self.deviceStates.workStopFlag:
            gevent.sleep(0.5)
        # 防止线程掉线
        camZmcThead = self.camZmcThead
        cbjZmcThead = self.cbjZmcThead
        cutterZmcThread = self.cutterZmcThread
        gevent.sleep(1)
        if camZmcThead.is_alive():
            common_tools.stop_thread(camZmcThead)
        if cbjZmcThead.is_alive():
            common_tools.stop_thread(cbjZmcThead)
        if cutterZmcThread.is_alive():
            common_tools.stop_thread(cutterZmcThread)
        self.logger.debug("检测到自动作业退出标志,强制结束各个作业线程,上料:{},裁切:{},铡刀:{}".format(self.camZmcIndex,
                                                                                                       self.cbjZmcIndex,
                                                                                                       self.cutterZmcIndex))
    def cam_zmc(self):
        self.camZmc.set_modbus_bit(210, 1)
        while True:
            # 1.等待允许拍照的信号
            ret, val = self.camZmc.get_allow_get_pic_state()
            if val == 1:
                self.camZmc.set_modbus_bit(15, 0)
                self.camZmc.set_modbus_bit(16, 1)
                gevent.sleep(10)
                self.camZmc.set_modbus_bit(19, 1)
            gevent.sleep(0.5)
            continue

    def web_request_cut_rect(self, pointList):
        """
        前端界面请求切矩形,不带台面补偿的
        :param pointList: 点的坐标列表[x1,y1,x2,y2]
        :return:
        """
        x1, y1, x2, y2 = pointList
        x1 = float(x1)
        y1 = float(y1)
        x2 = float(x2)
        y2 = float(y2)
        self.cbjZmc.cut_one_line_without_capfile((x1, y1), (x1, y2), self.cutDeep)
        self.cbjZmc.cut_one_line_without_capfile((x1, y2), (x2, y2), self.cutDeep)
        self.cbjZmc.cut_one_line_without_capfile((x2, y2), (x2, y1), self.cutDeep)
        self.cbjZmc.cut_one_line_without_capfile((x2, y1), (x1, y1), self.cutDeep)

    def cbj_zmc(self):
        while True:
            ret, val = self.cbjZmc.get_modbus_bit(25, 1)
            if val == 1:
                self.cbjZmc.set_modbus_bit(25, 0)
                self.web_request_cut_rect([100, 100, 1100, 700])
                gevent.sleep(10)
                self.cbjZmc.set_modbus_bit(26, 1)
            gevent.sleep(0.5)
            continue

    def cutter_zmc(self):
        self.zhanshitoos = False
        while True:
            ret, val = self.cutterZmc.get_allow_take_pic_begin_check()
            if val == 1:
                self.cutterZmc.set_modbus_bit(20, 0)
                gevent.sleep(1)
                ret = self.cutterZmc.set_begin_check_angle_ok()
                self.zhanshitoos = True
                while self.zhanshitoos:
                    ret, val = self.cutterZmc.get_allow_take_pic_final_check()
                    if val == 1:
                        self.cutterZmc.set_modbus_bit(25, 0)
                        ret = self.cutterZmc.set_final_check_result_ok()
                        gevent.sleep(2)
                        ret, info = self.cutterZmc.move_cutter_knife(float(-10))
                        self.zhanshitoos = False
                    gevent.sleep(0.5)
                    continue
            gevent.sleep(0.5)
            continue


if __name__ == '__main__':
    pass
